sthen@ told that it would be nice to have a proof tool that should
be used before sending a port to ports@. Maybe "portimport" is not
a very good name for a such tool, but that's a separate case. :)
Maybe this should be split in portcheck(1) and portimport(1)...
I think of this tool not only as "cvs import" helper (which is
handy), but as time-saving reviewer's tool.[1]

So here is even more revamped portimport(1), that tries to be as
strict as we usually are. Comparing to previous version I've sent,
It now supports more than one layer of
nesting ports. But the main feature is that it could catch not only
some often seen errors in ports themselves, but also it warns about
any extra file that should not be there.

New check added is for DISTFILES: if it starts with digit, then it
should either have {url} postfix, or DIST_SUBDIR should be set.

The usecase is:
  1) Run portimport.
  2) Fix errors, if any.
  3) If portimport still complains, use the "-f" flag: useful when,
     e.g.,, you really want a directory named "core".

Oh, and this tool already helped to find a few glitches in my ports.
Ideas for more checks we usually do before import are welcome. :)

--
  WBR,
    Vadim Zhukov

[1] At least you can treat this as my gift to landry@, who still
kindly reviews almost all of my ports. :)


Index: bin/portimport
===================================================================
RCS file: /cvs/ports/infrastructure/bin/portimport,v
retrieving revision 1.2
diff -u -p -r1.2 portimport
--- bin/portimport      11 Apr 2013 15:18:00 -0000      1.2
+++ bin/portimport      14 Aug 2013 09:30:11 -0000
@@ -2,6 +2,7 @@
 #
 # $OpenBSD: portimport,v 1.2 2013/04/11 15:18:00 zhuk Exp $
 # Copyright (c) 2013 Robert Peichaer
+# Copyright (c) 2013 Vadim Zhukov
 # 
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -18,52 +19,447 @@
 # Based on Marc Espie's portimport.
 # sthen: Modified to handle imports from mystuff/ and do a dry run first.
 # rpe:   rewrite based on sthen@'s version
+# zhuk:  rewrite based on rpe@'s version
 
 set -e
 
 usage() {
-       echo "usage: $(basename $0) [-u username]" >&2
+       echo "usage: ${0##*/} [-f] [-p portsdir] [-u username]" >&2
        exit 1
 }
 
+
+############################################################
+# Parsing command line options
+#
+
 user=$(id -un)
+force=false
+portsdir=
 
-while getopts "u:" OPT; do
+while getopts "fp:u:" OPT; do
        case $OPT in
-       u)      user="$OPTARG";;
-       *)      usage;;
+       f)
+               force=true
+               ;;
+
+       p)
+               set -f
+               if [ "${PWD##$OPTARG}" == "${PWD}" ]; then
+                       cat >&2 <<EOE
+${0##*/}: current directory does not seem to be under the
+specified root directory: $OPTARG
+EOE
+                       exit 3
+               fi
+               set +f
+               portsdir="$OPTARG"
+               ;;
+
+       u)
+               user="$OPTARG"
+               ;;
+
+       *)
+               usage
+               ;;
        esac
 done
 
-cvsroot=$u...@cvs.openbsd.org:/cvs
+
+############################################################
+# Detect path to root of directory tree of current port(-s) and put it
+# in $portsdir, unless it was set by user above. As a last resort, we
+# use some heuristics based on the commonly used names.
+#
+# We also have a $pkgpath variable, that represents subdirectory under
+# root ports directory where the port(-s) will be imported. In case we
+# use heuristics for determining $portsdir, we'll set up $pkgpath, too,
+# since we would get this info anyway.
+#
+# In make_args we write PORTSDIR_PATH override, that allows us to run
+# even in ports directory that is not on the PORTSDIR_PATH. This is
+# useful, for example, when you polish your port on cvs.openbsd.org,
+# where you cannot just override mk.conf.
+#
+
+pkgpath=
+make_args=MASTER_SITE_OPENBSD=
+
+if [[ -z $portsdir ]]; then
+       set +e
+       portsdir=$(make -V PORTSDIR 2>/dev/null)
+       (($? == 0)) && portsdir=
+       set -e
+fi
+
+if [[ -z $portsdir ]]; then
+       # heuristics mode ON
+       pkgpath="${PWD##*/ports/*(mystuff/|openbsd-wip/|p5-ports-wip/)}"
+       set -f
+       portsdir="${PWD%/$pkgpath}"
+       set +f
+
+       # This way we can run all checks even on cvs.openbsd.org
+       make_args="PORTSDIR_PATH=$portsdir:$(cd /usr/ports && make -V 
PORTSDIR_PATH || true)"
+fi
+
+if [[ -z $portsdir ]]; then
+       cat >&2 <<EOE
+${0##*/}: could not detect root ports directory. Please provide
+one with -p option.
+EOE
+       exit 2
+fi
+
+############################################################
+# Check and fail routines
+#
+
 error=false
-fulldir=$(pwd)
-importname="ports/${fulldir##*/ports/*(mystuff/|openbsd-wip/|p5-ports-wip/)}"
-timestamp=$(date '+%Y%m%d')
 
-err() { echo "$*"; error=true; }
+err() {
+       echo "$@" >&2
+       error=true
+}
+
+err_duplicated() {
+       err "$2 has \"$1\", as well as one of the parent directories"
+}
+
+err_core_found() {
+       err "file or directory name \"core\" found in $1, CVS will ignore it"
+}
+
+err_coredump_found() {
+       err "core dump file found in $1"
+}
+
+is_vcs_item() {
+       [[ $1 == @(.git|.hg|.svn|CVS|.fossil) ]]
+}
+
+handle_extra_file() {
+       # avoid warning, e.g., about ".*"
+       test -e "$1" || return 0
+
+       if is_vcs_item "${1##*/}"; then
+               err "VCS item detected: $1"
+       elif [[ ${1##*/} == core ]]; then
+               err_core_found "${1%/*}"
+       elif [[ $1 == *.core ]]; then
+               err_coredump_found "${1%/*}"
+       else
+               err "extra file: $1"
+       fi
+}
+
+check_port_hier() {
+       local pkg_lives_upper=$1; shift
+       local distinfo_lives_upper=$1; shift
+
+       local pkg_exists=false
+       [[ -d $1/pkg ]] && pkg_exists=true
+       $pkg_exists && $pkg_lives_upper &&
+               err_duplicated pkg/ "$1"
+       $pkg_lives_upper && pkg_exists=true
+
+       local distinfo_exists=false
+       [[ -f $1/distinfo ]] && distinfo_exists=true
+       $distinfo_exists && $distinfo_lives_upper &&
+               err_duplicated distinfo "$1"
+       $distinfo_lives_upper && distinfo_exists=true
+
+       local F
+       local npkgpath
+
+       for F in "$1"/* "$1"/.*; do
+               F="${F#./}"
+               if is_vcs_item "${F##*/}"; then
+                       err "VCS item detected: $F"
+               elif [[ -d $F ]]; then
+                       case "${F##*/}" in
+                       files)
+                               check_files_dir "$F"
+                               ;;
+
+                       patches)
+                               check_patches_dir "$F"
+                               ;;
+
+                       pkg)
+                               check_pkg_dir "$F"
+                               ;;
+
+                       *)
+                               [[ ${F##*/} == core ]] && err_core_found "$F"
+                               npkgpath=${npkgpath:-$(cd -- "$F" && make 
$make_args show=PKGPATH 2>/dev/null || true)}
+                               check_port_dir $pkg_exists $distinfo_exists "$F"
+                               ;;
+                       esac
+               else
+                       case "${F##*/}" in
+                       Makefile?(.inc)|*.port.mk)
+                               check_makefile "$F"
+                               ;;
+
+                       distinfo)
+                               ;;
+
+                       *)
+                               handle_extra_file "$F"
+                               ;;
+                       esac
+               fi
+       done
+       pkgpath=${pkgpath:-${npkgpath%/*}}
+       egrep -q '^[[:space:]]*SUBDIR[[:space:]]*=' Makefile ||
+               err missing subdir Makefile
+}
+
+check_port_dir() {
+       local pkg_lives_upper=$1; shift
+       local distinfo_lives_upper=$1; shift
+
+       if [[ -f $1/Makefile.inc ]]; then
+               check_port_hier $pkg_lives_upper $distinfo_lives_upper "${1#./}"
+               return
+       fi
+
+       local F
+       local distinfo_exists=false
+       local mk_exists=false
+       local pkg_exists=false
+
+       for F in "$1"/* "$1"/.*; do F="${F#./}"; case "${F##*/}" in
+       Makefile)
+               test -f "$F" || err "$F is not a file"
+               check_makefile "$F"
+               mk_exists=true
+               ;;
+
+       distinfo)
+               $distinfo_lives_upper && err_duplicated distinfo "$1"
+               distinfo_exists=true
+               test -f "$F" || err "$F is not a file"
+               ;;
+
+       *.port.mk)
+               test -f "$F" || err "$F is not a file"
+               check_makefile "$F"
+               ;;
 
-[[ -f Makefile && -f distinfo && -f pkg/DESCR  && -f pkg/PLIST ]] || err "No 
ports files?"
-find . -name .git          -print|read i && err "You git!"
-find . -name .\*.swp       -print|read i && err "Found vim swap file"
-find . -name \*.orig       -print|read i && err "Found .orig file, ouch"
-find . -name typescript    -print|read i && err "Found typescript file, ouch"
-find . -path ./w-\*        -print|read i && err "Please wipe out work 
directory before importing"
-find . -type d -name core  -print|read i && err "directory named core found, 
cvs will ignore it"
-find . -type f -name .todo -print|read i && err "devtodo file found"
-find . -type d -name CVS   -print|read i && err "Some CVS stuff already in 
there, very funky"
-$error && exit 1
+       systrace.filter)
+               test -f "$F" || err "$F is not a file"
+               ;;
+
+       files)
+               if [[ -d $F ]]; then
+                       check_files_dir "$F"
+               else
+                       err "$F" is not a directory
+               fi
+               ;;
+
+       patches)
+               if [[ -d $F ]]; then
+                       check_patches_dir "$F"
+               else
+                       err "$F" is not a directory
+               fi
+               ;;
+
+       pkg)
+               $pkg_lives_upper && err_duplicated pkg/ "$1"
+               pkg_exists=true
+               if [[ -d $F ]]; then
+                       check_pkg_dir "$F"
+               else
+                       err "$F" is not a directory
+               fi
+               ;;
+
+       *)
+               handle_extra_file "$F"
+               ;;
+       esac; done
+
+       $mk_exists || err no Makefile in "$1"
+
+       $pkg_lives_upper && pkg_exists=true
+       $pkg_exists || err "no pkg/ in $1"
+
+       $distinfo_lives_upper && distinfo_exists=true
+       $distinfo_exists || err "no distinfo in $1"
+
+       local show_items="SHARED_LIBS DISTFILES DIST_SUBDIR"
+       local shlibs distfiles dist_subdir master_sites
+
+        # Do not try to use co-processes, there is some bug related
+        # to redirection of error stream seen on big number of
+        # nested ports (100 or so). and we need to redirect &2 to
+        # avoid noising about accessing dead co-processes.
+
+       (cd "$1"; make $make_args show="$show_items" || true) | {
+               read shlibs
+               read distfiles
+               read dist_subdir
+
+               check_shlibs "$1" $shlibs
+               check_distfiles "$1" "$dist_subdir" $distfiles
+       }
+}
+
+check_shlibs() {
+       local dir="$1"; shift
+       local lib
+       local libver
+
+       local portref=
+       [[ $dir != . ]] && portref="in \"${dir#./}\" port "
+       while (($# > 1)); do
+               lib=$1
+               libver=$2
+               if [[ $libver != 0.0 ]]; then
+                       err "${portref}library $lib has version $libver" \
+                           "instead of 0.0"
+               fi
+               shift 2
+       done
+}
+
+check_distfiles() {
+       local dir="$1"; shift
+       local dist_subdir="$1"; shift
+
+       # do not care about absent distfiles, this is fine for meta ports
+       while (($# > 1)); do
+               if [[ $1 == [0-9]* && -z $dist_subdir && $1 != *\{*\} ]]; then
+                       err "badly named distfile $1 without DIST_SUBDIR" \
+                           "or {url} postfix"
+               fi
+               shift
+       done
+}
+
+check_files_dir() {
+       if (($(ls -A "$1" | wc -l) == 0)); then
+               err "there are no files, please remove the $1 directory instead"
+               return
+       fi
+
+       find "$1" -type f -name *.core -print | read i &&
+               err_coredump_found "$1"
+}
+
+check_patches_dir() {
+       local empty=true
+       local F
+
+       for F in "$1"/* "$1"/.*; do case "${F##*/}" in
+       patch-*.orig)
+               handle_extra_file "$F"
+               ;;
+
+       patch-*)
+               empty=false
+               test -f "$F" || err "$F is not a file"
+               ;;
+
+       *)
+               handle_extra_file "$F"
+               ;;
+       esac; done
+
+       $empty && err "there are no patches, please remove the $1 directory 
instead"
+}
+
+check_pkg_dir() {
+       local empty=true
+       local F
+
+       for F in "$1"/* "$1"/.*; do case "${F##*/}" in
+       DESCR?(-*)|PLIST?(-*))
+               empty=false
+               test -f "$F" || err "$F" is not a file
+               ;;
+
+       MESSAGE?(-*)|PFRAG.*|README?(-*)|SECURITY?(-*)|UNMESSAGE?(-*)|*.rc)
+               empty=false
+               test -f "$F" || err "$F" is not a file
+               ;;
+
+       *)
+               handle_extra_file "$F"
+               ;;
+       esac; done
+
+       $empty && err "$1 directory does not contain any DESCR or PLIST files"
+}
+
+check_makefile() {
+       grep -q ^REVISION "$1" 2>/dev/null &&
+               err "REVISION(-s) found in $1"
+}
+
+
+############################################################
+# Run checks and calculate pkgpath variable, that represents
+# subdirectory under root ports directory where the port(-s)
+# will be imported.
+#
+
+pkgpath=${pkgpath:-$(make $make_args show=PKGPATH 2>/dev/null || true)}
+check_port_dir false false .
+
+if [[ -z $pkgpath ]]; then
+       if [[ -n $portsdir ]]; then
+               set -f
+               pkgpath="${PWD##$portsdir/}"
+               set +f
+       else
+               # pure heuristics
+               pkgpath="${PWD##*/ports/*(mystuff/|openbsd-wip/|p5-ports-wip/)}"
+       fi
+fi
+
+if [ X"$pkgpath" == X"$PWD" ]; then
+       cat >&2 <<EOE
+${0##*/}: could not determine PKGPATH. Please help me with the -p option.
+EOE
+       exit 2
+fi
+
+if $error; then
+       if $force; then
+               echo "WARNING: forcing import even with warnings above" >&2
+       else
+               echo "ERROR: no import due to the problems mentioned above" >&2
+               exit 1
+       fi
+fi
+
+
+############################################################
+# All checks are completed, start actual import process
+#
+
+timestamp=$(date '+%Y%m%d')
+if [[ $(hostname) == cvs.openbsd.org ]]; then
+       cvsroot=/cvs
+else
+       cvsroot=$u...@cvs.openbsd.org:/cvs
+fi
 
 echo -n "Import would go into: "
-cvs -n -d$cvsroot import $importname $user ${user}_$timestamp 2>/dev/null | \
+cvs -n -d$cvsroot import ports/$pkgpath $user ${user}_$timestamp 2>/dev/null | 
\
        grep Makefile | head -1 | awk '{print $2}' | xargs dirname
 
 read ans?'Correct path? [y/n] '
 if [[ $ans == +(y|Y) ]]; then
-       cvs -d$cvsroot import $importname $user ${user}_$timestamp
-       cd /usr/$importname/../
-       cvs -d$cvsroot update -AdP ${fulldir##*/}
+       cvs -d$cvsroot import ports/$pkgpath $user ${user}_$timestamp
+       cd "$portsdir/${pkgpath%/*}"
+       cvs -d$cvsroot update -AdP ${pkgpath##*/}
        echo "Don't forget to commit the category Makefile when you're done!"
-       cd /usr/$importname/../
        pwd
 fi
Index: man/man1/portimport.1
===================================================================
RCS file: /cvs/ports/infrastructure/man/man1/portimport.1,v
retrieving revision 1.2
diff -u -p -r1.2 portimport.1
--- man/man1/portimport.1       11 Apr 2013 15:18:00 -0000      1.2
+++ man/man1/portimport.1       14 Aug 2013 09:30:11 -0000
@@ -22,6 +22,8 @@
 .Nd import a new port to the ports cvs repository
 .Sh SYNOPSIS
 .Nm
+.Op Fl f
+.Op Fl p Ar portsdir
 .Op Fl u Ar username
 .Sh DESCRIPTION
 .Nm
@@ -47,8 +49,36 @@ ports cvs repository.
 After the import, the new port is checked out in the respective directory
 of the local ports tree.
 .Pp
+By default,
+.Nm
+automatically picks up any directory named
+.Dq ports ,
+with an optional
+.Dq mystuff ,
+.Dq openbsd-wip
+or
+.Dq p5-ports-wip
+subdirectory component, as the ports root directory.
+.Pp
+For example: if the port being imported is located in
+.Pa /home/joe/cvs/ports/p5-ports-wip/devel/p5-Foo ,
+then the the root ports directory will be detected as being
+.Pa /home/joe/cvs/ports/p5-ports-wip
+automagically.
+To override this behaviour, see the
+.Fl p
+option description below.
+.Pp
 The following options are available:
 .Bl -tag -width Ds
+.It Fl f
+Forces import even if some checks fail.
+Should be used in corner cases, e.g., when moving ports in tree.
+.It Fl p Ar portsdir
+Forces the given directory to be treated as ports root directory.
+Cancels autodetection of the root ports directory made by default.
+This option is useful, e.g., when you have a temporary ports tree in
+a non-standard location.
 .It Fl u Ar username
 Set the username used for
 .Xr ssh 1 ,

Reply via email to