aboutsummaryrefslogblamecommitdiff
path: root/Tools/scripts/rmport
blob: 7e6c5ef7a99bc1dd0c028c7e86406d5fe6665c05 (plain) (tree)
1
2
3
4
5



                                                            
                                 













































                                                                             






                                                               




                                           











                                                               























                                                                            






                                                                                                             




                                                                                           






                                          






                                                                              






                                           

                                                                 


                                              

                                                                              
                
 
                    
                    







                                                                              
 




                                                                          
                                  













                                                                         
                               











                     
                    
                                                       


                     
                                                                            




                                          
                                             
 



                                              




                                                                              
                                                                                                       
                                                                            




                                                                  


            

                                                                    






















                                                                                            



















                                                                        























                                                                                                 












                                                                                      

                    
 

                                                                           

                                                          


                                                                       




                                                                                     




                                    


                                 
                    
 
                                                                   
                                                 




                                                                              
 
                                                     
 
                                                            









                                                           


                                                                                                    





                                     
                    
 
                                               
 
                                                                                      

 




















                                                                           






                             
                                                    
 
                                                           
 

                                                                             


                       
                                         

        
                                
                                                  
 
                                             
 
                                       
                                











                         


                                                      









                                    
                   

                                      
                   

                                                               



                                                                

                                           


                                                       
 




               
                                                              






                                
                                   
                                         


            







                                







                                



                                
                                                           


            





                      

                                         

                                  

                                     
                                    
 
                                     
 



                                                

                              
                                         


                                             
                             


                                    
                          

                                  

    



                                                                          
                    
 


                                                                              
                   
                                                                 

                                                        





                           
#!/bin/sh -e
#
# rmport - remove port(s) from the FreeBSD Ports Collection.
#
# Copyright 2006-2007 Vasil Dimov
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# Authors:
# Originally written by Vasil Dimov <vd@FreeBSD.org>
# Others:
#
# $FreeBSD$
#
# MAINTAINER=	vd@FreeBSD.org
#

PORTSDIR=${PORTSDIR:-/usr/ports}
INDEX=${PORTSDIR}/`make -C ${PORTSDIR} -V INDEXFILE`

TODAY=`date -u -v+0d +%Y-%m-%d`

SED="sed -i .orig -E"
# use ~/.ssh/config to set up the desired username if different than $LOGNAME
PCVS=${PCVS:-cvs -d pcvs.freebsd.org:/home/pcvs}

log()
{
	echo "==> $*" >&2
}

escape()
{
	# escape characters that may appear in ports' names and
	# break regular expressions
	echo "${1}" |sed -E 's/(\+|\.)/\\\1/g'
}

pkgname()
{
	make -C ${PORTSDIR}/${1} -V PKGNAME
}

ask()
{
	question=${1}

	answer=x
	while [ "${answer}" != "y" -a "${answer}" != "n" ] ; do
		read -p "${question} [yn] " answer
	done

	echo ${answer}
}

# return category/port if arg is directly port's directory on the filesystem
find_catport()
{
	arg=${1}

	if [ -d "${PORTSDIR}/${arg}" ] ; then
		# arg is category/port
		echo ${arg}
	elif [ -d "${arg}" ] ; then
		# arg is the port's directory somewhere in the filesystem
		# either absolute or relative

		# get the full path
		rp=`realpath ${arg}`

		category=`basename \`dirname ${rp}\``
		port=`basename ${rp}`
		echo ${category}/${port}
	else
		echo "What do you mean by \`${arg}'?" >&2
		exit 1
	fi
}

find_expired()
{
	EXPVAR=EXPIRATION_DATE

	find ${PORTSDIR} -mindepth 3 -maxdepth 3 -name "Makefile*" \
		|xargs grep -H ${EXPVAR} \
		|sed -E "s|${PORTSDIR}/?([^/]+/[^/]+)/Makefile:${EXPVAR}=[[:space:]]*([0-9-]{10})$|\2 \1|g" \
		|perl -ne "if ((substr(\$_, 0, 10) cmp '${TODAY}') <= 0) { print(\$_); }" \
		|while read expdate catport ; do \
			echo -n "${expdate} ${catport}: " ; \
			make -C ${PORTSDIR}/${catport} -V DEPRECATED ; \
		done
}

# create temporary checkout directory
mkcodir()
{
	log "creating temporary directory"
	d=`mktemp -d -t rmport`
	mkdir ${d}/CVS
	cat > ${d}/CVS/Repository <<REPOSITORY
We need ./CVS directory in order to create a custom commit message template
(and put it in ./CVS/Template). Anyway cvs insists on CVS/Repository existence
although it (hopefully) does not care about its contents.
REPOSITORY
	touch ${d}/CVS/Template
	log "created ${d}"
	echo "${d}"
}

# checkout common files from the repository
co_common()
{
	log "getting ports/MOVED and ports/LEGAL from repository"
	${PCVS} co ports/MOVED ports/LEGAL
}

# check if some ports depend on the given port
# XXX Very Little Chance (tm) for breaking INDEX exists:
# /usr/ports/INDEX may be outdated and not contain recently added dependencies
check_dep_core()
{
	catport=${1}
	alltorm=${2}
	pkgname=`pkgname ${catport}`

	rmpkgs=""
	rmcatports=""
	for torm in ${alltorm} ; do
		rmpkgs="${rmpkgs:+${rmpkgs}|}`pkgname ${torm}`"
		rmcatports="${rmcatports:+${rmcatports}|}${PORTSDIR}/${torm}/"
	done

	err=0

	deps=`grep -E "${pkgname}" ${INDEX} |grep -vE "^(${rmpkgs})" || :`
	if [ -n "${deps}" ] ; then
		log "${catport}: some port(s) depend on ${pkgname}:"
		echo "${deps}" >&2
		err=1
	fi

	# check if some Makefiles mention the port to be deleted
	portdir_grep="^[^#].*/`basename ${catport}`([[:space:]]|/|$)"
	r="`find ${PORTSDIR} -mindepth 2 -maxdepth 3 \
		\( -name "Makefile*" -or -path "*Mk/*.mk" \) \
		|xargs grep -EH "${portdir_grep}" \
		|grep -vE "^(${rmcatports})" || :`"
	if [ -n "${r}" ] ; then
		if [ ${err} -eq 1 ] ; then
			echo >&2
		fi
		log "${catport}: some Makefiles mention ${portdir_grep}:"
		echo "${r}" >&2
		err=1
	fi

	return ${err}
}

check_dep()
{
	catport=${1}
	persist=${2}
	alltorm=${3}

	while : ; do
		log "${catport}: checking dependencies"

		err=0

		res="`check_dep_core ${catport} "${alltorm}" 2>&1`" || err=1

		if [ ${err} -eq 0 ] ; then
			break
		fi

		echo "${res}" |${PAGER:-less}

		if [ ${persist} -eq 0 ] ; then
			break
		fi

		echo "" >&2
		echo "you can ignore the above issues and proceed anyway." >&2
		echo "if this is the case, then either:" >&2
		echo " * these are false positives" >&2
		echo " * you want to break something" >&2
		echo " * your ${PORTSDIR} is out of date, consider setting PORTSDIR in environment" >&2
		echo "   to point to a newer instance of the ports tree" >&2
		echo "or you can hit \`n' to repeat the check" >&2
		answer=`ask "ignore the above issues"`
		if [ "${answer}" = "y" ] ; then
			break
		fi
	done
}

# query GNATS via query-pr-summary.cgi, format and return the result
get_PRs_www()
{
	catport=${1}
	synopsis=${2}
	date_re='[0-9]{4}/[0-9]{2}/[0-9]{2}'
	prnum_re='.+/[0-9]{5,6}'
	synopsis_re='.{10,}'

	log "${catport}: getting PRs having ${synopsis} in the synopsis"

	url="http://www.freebsd.org/cgi/query-pr-summary.cgi?text=${synopsis}"

	raw="`fetch -q -T 20 -o - "${url}"`"

	if [ -z "${raw}" ] ; then
		log "${catport}: empty result from URL: ${url}"
		exit 1
	fi

	printf "%s" "${raw}" \
	|sed -nE "s|.*>(${date_re})<.*>(${prnum_re})<.*>(${synopsis_re})</td.*|\1 \2 \3|p" \
	|sort
}

# query GNATS via query-pr on freefall and return the result
get_PRs_freefall()
{
	catport=${1}
	synopsis=${2}

	log "${catport}: getting PRs having ${synopsis} in the synopsis"

	ssh freefall.freebsd.org "query-pr -qx -y '${synopsis}' || :"
}

# query GNATS and return the result
get_PRs()
{
	catport=${1}
	synopsis=${2}

	get_PRs_freefall ${catport} ${synopsis}
}

# check if any PRs exist that are related to the port
check_PRs()
{
	catport=${1}
	synopsis=${2}

	PRs="`get_PRs ${catport} "${synopsis}"`" || exit

	if [ -n "${PRs}" ] ; then
		log "${catport}: PRs found, related to ${synopsis}:"
		printf "%s\n" "${PRs}" >&2

		echo "you can skip ${catport} and continue with the rest or remove it anyway" >&2
		answer=`ask "do you want to skip ${catport}?"`
		if [ "${answer}" = "y" ] ; then
			return 1
		else
			return 0
		fi
	fi

	return 0
}

# checkout port's specific files from the repository
co_port()
{
	cat=${1}
	port=${2}

	log "${cat}/${port}: getting ${cat}/Makefile and port's files from repository"
	${PCVS} co ports/${cat}/Makefile ports/${cat}/${port}
}

# check if anything about the port is mentioned in ports/LEGAL
check_LEGAL()
{
	catport=${1}
	pkgname=${2}

	for checkstr in ${pkgname} ${catport} ; do
		msg="${catport}: checking if ${checkstr} is in ports/LEGAL"
		log "${msg}"
		while grep -i ${checkstr} ports/LEGAL ; do
			echo "" >&2
			echo "${checkstr} is in ${PWD}/ports/LEGAL" >&2
			echo "remove it and hit <enter> when ready" >&2
			echo "or hit \`s' to skip this issue and continue anyway" >&2
			read answer
			if [ "${answer}" = "s" ] ; then
				break
			fi
			log "${msg}"
		done
	done
}

# add port's entry to ports/MOVED
edit_MOVED()
{
	catport=${1}

	DEPRECATED="`make -C ${PORTSDIR}/${catport} -V DEPRECATED`"
	DEPRECATED=${DEPRECATED:+: ${DEPRECATED}}
	if [ -n "`make -C ${PORTSDIR}/${catport} -V EXPIRATION_DATE`" ] ; then
		REASON="Has expired${DEPRECATED}"
	else
		REASON="Removed${DEPRECATED}"
	fi

	log "${catport}: adding entry to ports/MOVED"

	echo "${catport}||${TODAY}|${REASON}" >> ports/MOVED
}

# remove port from category/Makefile
edit_Makefile()
{
	cat=${1}
	port=${2}

	log "${cat}/${port}: removing from ${cat}/Makefile"

	portesc=`escape ${port}`

	${SED} -e "/^[[:space:]]*SUBDIR[[:space:]]*\+=[[:space:]]*${portesc}([[:space:]]+#.*)?$/d" \
		ports/${cat}/Makefile
}

# remove port's files
rm_port()
{
	catport=${1}

	log "${catport}: removing port's files"

	${PCVS} rm `find ports/${catport} -type f -not -path "*/CVS/*" -delete -print`
}

append_Template()
{
	catport=${1}

	msg=${catport}

	EXPIRATION_DATE=`make -C ${PORTSDIR}/${catport} -V EXPIRATION_DATE`
	if [ -n "${EXPIRATION_DATE}" ] ; then
		msg="${EXPIRATION_DATE} ${msg}"
	fi

	DEPRECATED="`make -C ${PORTSDIR}/${catport} -V DEPRECATED`"
	if [ -n "${DEPRECATED}" ] ; then
		msg="${msg}: ${DEPRECATED}"
	fi

	log "${catport}: adding entry to commit message template"

	echo "${msg}" >> ./CVS/Template
}

# diff
diff()
{
	log "creating diff"

	diffout=${codir}/diff

	${PCVS} diff -u ports > ${diffout} 2>&1 || :

	read -p "hit <enter> to view cvs diff output" dummy

	# give this to the outside world so it can be showed to the committer
	# and removed when we are done
	echo ${diffout}
}

# update, ask for confirmation and commit
commit()
{
	log "running cvs update"
	${PCVS} -fnq up ports 2>&1 |${PAGER:-less}

	answer=`ask "do you want to commit?"`

	if [ "${answer}" = "y" ] ; then
		${PCVS} ci ports
	fi
}

cleanup()
{
	diffout=${1}
	codir=${2}

	log "cleaning up"

	rm ${diffout}

	rm CVS/Entries.Log CVS/Repository CVS/Template
	rmdir CVS

	# release cvs directories
	${PCVS} rel -d CVSROOT ports

	cd /
	rmdir ${codir}
}

usage()
{
	echo "Usage:" >&2
	echo "" >&2
	echo "find expired ports:" >&2
	echo "${0} -F" >&2
	echo "" >&2
	echo "remove port(s):" >&2
	echo "${0} category1/port1 [ category2/port2 ... ]" >&2
	echo "" >&2
	echo "remove all expired ports (as returned by -F):" >&2
	echo "${0} -a" >&2
	echo "" >&2
	echo "just check dependencies:" >&2
	echo "${0} -d category/port" >&2
	echo "" >&2
	echo "just check if any related PRs exist:" >&2
	echo "${0} -p synopsis" >&2

	exit 64
}

# main

if [ ${#} -eq 0 -o "${1}" = "-h" -o "${1}" = "--help" ] ; then
	usage
fi

if [ ${1} = "-d" ] ; then
	if [ ${#} -ne 2 ] ; then
		usage
	fi
	catport=`find_catport ${2}`
	check_dep ${catport} 0 ${catport}
	exit
fi

if [ ${1} = "-p" ] ; then
	if [ ${#} -ne 2 ] ; then
		usage
	fi
	get_PRs "dummy" ${2}
	exit
fi

if [ ${1} = "-F" ] ; then
	if [ ${#} -ne 1 ] ; then
		usage
	fi
	find_expired
	exit
fi

if [ ${1} = "-a" ] ; then
	if [ ${#} -ne 1 ] ; then
		usage
	fi
	${0} `find_expired |cut -f 2 -d ' ' |cut -f 1 -d :`
	exit
fi

codir=`mkcodir`
cd ${codir}

co_common

for catport in $* ; do
	# convert to category/port
	catport=`find_catport ${catport}`
	cat=`dirname ${catport}`
	port=`basename ${catport}`
	# remove any trailing slashes
	catport="${cat}/${port}"
	pkgname=`pkgname ${catport}`

	check_dep ${catport} 1 "${*}"

	if ! check_PRs ${catport} ${port} ; then
		continue
	fi

	co_port ${cat} ${port}

	check_LEGAL ${catport} ${pkgname}

	# everything seems ok, edit the files

	edit_MOVED ${catport}

	edit_Makefile ${cat} ${port}

	rm_port ${catport}

	append_Template ${catport}
done

# give a chance to the committer to edit files by hand and recreate/review
# the diff afterwards
answer=y
while [ "${answer}" = "y" ] ; do
	diffout=diff

	# EDITOR instead of PAGER because vim has nice syntax highlighting ;-)
	${EDITOR} ${diffout}

	echo "" >&2
	echo "you can now edit files under ${codir}/ by hand" >&2
	answer=`ask "do you want to recreate the diff?"`
done

commit

cleanup ${diffout} ${codir}

# EOF