Hello all.
So here is an attempt on separate portcheck(1) utility. It intended to
be run anytime you want: when creating a port, when updating it or to
run validate the whole tree. Manual page is at the end of this letter,
and actual code begins right after my signature. It currently handles
the following problems:
* extra REVISION marks
* non-0.0 SHARED_LIBS
* VCS leftovers and other extra files in port directories
* executable bits on files in port directories
* missing -guic and other FreeDesktop goo
* distfiles that need a DIST_SUBDIR or {url} syntax
* unreliable MASTER_SITES (blacklist, suggestions are welcome)
Also it could uncover some more problems that ports infrastructure will
report itself. Just run "cd /usr/ports && portcheck -A" and see...
The portcheck (tries to) correctly handles nested hierarchies of ports,
module-only port directories (like www/mozilla), placing of distinfo
and PLIST files upper in hierarchy and so on.
It also tries to probe different FLAVORs, altough the logic there could
be improved. But even in current design, it discovered, for example,
missing licensing info for "compface" FLAVOR in mail/claws-mail.
I'll prepare and send patches for some of the detected errors a bit
later. Hope you'll enjoy this tool. After positive feedback on previous
attempts for portimport(1), I'm looking for okays to import portcheck(1)
under ports/infrastructure/bin/.
--
WBR,
Vadim Zhukov
#!/bin/ksh
#
# $OpenBSD$
# 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
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
set -e
set +X
usage() {
echo "usage: ${0##*/} [-ACdPU] [-p portsdir] [-x glob]" >&2
exit 1
}
############################################################
# Parsing command line options
#
existing_port=false
ignore_cvs=false
plist_checks=true
portsdir=
rootrun=false
unset ignore_list
debugging=false
while getopts "ACdPp:Ux:" OPT; do
case $OPT in
A)
$rootrun || set -A ignore_list -- "${ignore_list[@]}" \
.cvsignore \
INDEX \
README \
bulk \
distfiles \
infrastructure \
logs \
lost+found \
mystuff \
openbsd-wip \
p5-ports-wip \
packages \
plist \
pobj \
update
rootrun=true
existing_port=true
ignore_cvs=true
;;
C)
ignore_cvs=true
;;
d)
debugging=true
;;
P)
plist_checks=false
;;
p)
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
portsdir=$OPTARG
;;
U)
existing_port=true
;;
x)
set -A ignore_list -- "${ignore_list[@]}" "$OPTARG"
;;
*)
usage
;;
esac
done
shift $(($OPTIND - 1))
(($# > 0)) && usage
############################################################
# 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 check your port on cvs.openbsd.org,
# where you cannot just override mk.conf.
#
pkgpath=
if [[ -z $portsdir ]]; then
# heuristics mode ON
pkgpath=${PWD##*/ports/*(mystuff/|openbsd-wip/|p5-ports-wip/)}
portsdir=${PWD%"/$pkgpath"}
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
# This way we can run all checks even on cvs.openbsd.org
set -A make_args -- MASTER_SITE_OPENBSD= \
PORTSDIR_PATH="$portsdir:$(cd /usr/ports && make -V PORTSDIR_PATH ||
true)"
############################################################
# Check and fail routines
#
error=false
err() {
echo "$@" >&2
error=true
}
err_duplicated() {
err "both $2 and some of its parents has $1"
}
err_core_found() {
err "file or directory \"$1\" found, CVS will ignore it"
}
err_coredump_found() {
err "core dump file found: $1"
}
is_vcs_item() {
[[ -d "$1" && ${1##*/} == @(CVS|.fslckout|.git|.hg|.svn) ]]
}
ignoring() {
local iglob
for iglob in "${ignore_list[@]}"; do
[[ $1 == $iglob ]] && return 0
done
return 1
}
handle_extra_file() {
ignoring "$1" || return 0
# avoid warning, e.g., about ".*"
test -e "$1" || return 0
if is_vcs_item "$1"; then
if ! $ignore_cvs || [[ ${1##*/} != CVS ]]; then
err "VCS item detected: $1"
fi
elif [[ ${1##*/} == core ]]; then
$rootrun || err_core_found "${1%/*}"
elif [[ -f $1 && $1 == *.core ]]; then
err_coredump_found "$1"
elif [[ -d $1 ]]; then
err "extra directory: $1"
else
err "extra file: $1"
fi
}
# Print out a ref to the particular subport/subpackage, if needed.
# Port FLAVORs could also be handled, if provided.
portref() {
local dir=$1; shift
local subpkg=
if (($# > 0)); then
subpkg=$1
shift
fi
# the rest is a list of FLAVORs
local ref=
if [[ $dir != . ]]; then
ref="${dir#./}"
[[ -n $subpkg && $subpkg != "-" ]] && ref="$ref,$subpkg"
else
[[ $subpkg != "-" ]] && ref="$subpkg"
fi
# assume all given FLAVORs are different
if (($# > 1)) || [[ -n $1 ]]; then
[[ -n $ref ]] && ref="$ref, "
ref="${ref}FLAVOR \""
local first_flavor=true
while (($# > 0)); do
$first_flavor || ref="$ref, "
ref="$ref$1"
first_flavor=false
shift
done
ref="$ref\""
fi
[[ -n $ref ]] && echo "in $ref: "
}
# Checks made:
# * Whitelist filter of what could be in this directory.
check_port_hier() {
$debugging && echo "CALLED: check_port_hier($*)" >&2
local dir=$1; shift
for opt; do
# looks unsafe but we do not pass anything except
# "foo=true" and "foo=false" here
eval "$opt"
shift
done
local distinfo_lives_upper=${distinfo_lives_upper:-false}
local pkg_lives_upper=${pkg_lives_upper:-false}
local plist_lives_upper=${plist_lives_upper:-false}
local distinfo_exists=false
[[ -f $dir/distinfo ]] && distinfo_exists=true
$distinfo_exists && $distinfo_lives_upper &&
err_duplicated distinfo "$dir"
local pkg_exists=false
[[ -d $dir/pkg ]] && pkg_exists=true
local plist_exists=false
ls $dir/pkg/PLIST* >/dev/null 2>&1 && plist_exists=true
$plist_lives_upper && $plist_exists &&
err_duplicated "packing list(-s)" "$dir"
$distinfo_lives_upper && distinfo_exists=true
$pkg_lives_upper && pkg_exists=true
$plist_lives_upper && plist_exists=true
local recursive_args
set -A recursive_args -- \
distinfo_lives_upper=$distinfo_exists \
pkg_lives_upper=$pkg_exists \
plist_lives_upper=$plist_exists
local F
for F in "$dir"/* "$dir"/.*; do
F=${F#./}
ignoring "$F" && continue
if is_vcs_item "$F"; then
if ! $ignore_cvs || [[ ${F##*/} != CVS ]]; then
err "VCS item detected: $F"
fi
elif [[ -d $F ]]; then
case "${F##*/}" in
files|patches|pkg)
check_${F##*/}_dir "$F"
;;
patches?(-*))
check_patches_dir "$F"
;;
*)
if ! $rootrun && [[ ${F##*/} == core ]]; then
err_core_found "$F"
fi
if ! [[ -f $F/Makefile ]]; then
# Avoid extra spam
err "not a port directory: $F"
else
local pkgpath_set=false
[[ -n $pkgpath ]] && pkgpath_set=true
check_port_dir "$F"
"${recursive_args[@]}"
$pkgpath_set || pkgpath=${pkgpath%/*}
fi
;;
esac
else
case "${F##*/}" in
Makefile?(.inc)|*.port.mk)
check_makefile "$F"
;;
distinfo)
;;
*)
handle_extra_file "$F"
;;
esac
fi
done
egrep -q '^ *SUBDIR[[:space:]]*\+?=' Makefile ||
err missing subdir Makefile
}
# Checks made:
# * Whitelist filter of what could be in this directory.
check_port_dir() {
$debugging && echo "CALLED: check_port_dir($*)" >&2
local dir=$1; shift
for opt; do
# looks unsafe but we do not pass anything except
# "foo=true" and "foo=false" here
eval "$opt"
shift
done
local distinfo_lives_upper=${distinfo_lives_upper:-false}
local pkg_lives_upper=${pkg_lives_upper:-false}
local plist_lives_upper=${plist_lives_upper:-false}
check_perms_in_dir "$dir"
if [[ -f $dir/Makefile.inc ]] ||
egrep -sq '^ *SUBDIR[[:space:]]*\+?=' "$dir"/Makefile; then
check_port_hier "${dir#./}" "$@"
return
fi
local F
local distinfo_exists=false
local mk_exists=false
local pkg_exists=false
local plist_exists=false
local portmk_exists=true
local non_portmk=0
for F in "$dir"/* "$dir"/.*; do
F=${F#./}
ignoring "$F" && continue
case ${F##*/} in
Makefile)
test -f "$F" || err "$F is not a file"
check_makefile "$F"
mk_exists=true
((non_portmk++)) || true
;;
distinfo)
$distinfo_lives_upper && err_duplicated distinfo "$dir"
distinfo_exists=true
test -f "$F" || err "$F is not a file"
((non_portmk++)) || true
;;
*.port.mk)
test -f "$F" || err "$F is not a file"
check_makefile "$F"
portmk_exists=true
;;
systrace.filter)
test -f "$F" || err "$F is not a file"
((non_portmk++)) || true
;;
files|patches)
if [[ -d $F ]]; then
check_${F##*/}_dir "$F"
else
err "$F" is not a directory
fi
((non_portmk++)) || true
;;
pkg)
if [[ -d $F ]]; then
check_pkg_dir "$F"
pkg_exists=true
ls "$F"/PLIST* >/dev/null 2>&1 &&
plist_exists=true
$plist_lives_upper && $plist_exists &&
err_duplicated "packing list(-s)" "$dir"
else
err "$F" is not a directory
fi
((non_portmk++)) || true
;;
*)
handle_extra_file "$F"
;;
esac
done
# examples: lang/clang, www/mozilla
$portmk_exists && ((non_portmk == 0)) && return
$mk_exists || err no Makefile in "$dir"
$pkg_lives_upper && pkg_exists=true
$pkg_exists || err "no pkg/ in $dir"
$distinfo_lives_upper && distinfo_exists=true
$distinfo_exists || $existing_port || err "no distinfo in $dir"
# Now gather and check some info via "make show=...".
# We request all info at once for speed.
local dist_subdir distfiles flavor flavors master_sites
local multi_packages pseudo_flavors shared_libs
local show_items="DIST_SUBDIR DISTFILES FLAVOR FLAVORS MASTER_SITES"
local show_items="$show_items MULTI_PACKAGES PSEUDO_FLAVORS SHARED_LIBS"
# 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 stderr to
# avoid noise when accessing dead co-processes accidentially.
(cd -- "$dir"; make "${make_args[@]}" show="$show_items") | {
read dist_subdir
read distfiles
read flavor
read flavors
read master_sites
read multi_packages
read pseudo_flavors
read shared_libs
set -A check_flavors --
[[ -z $flavor ]] && set -A check_flavors -- ""
local f pf
for f in $flavors; do
for pf in $pseudo_flavors; do
[[ $f == "$pf" ]] && continue 2
done
[[ $f == debug ]] && continue # XXX
set -A check_flavors -- "${check_flavors[@]}" $f
done
check_distfiles "$dir" "$dist_subdir" $distfiles
check_master_sites "$dir" $master_sites
$existing_port || check_shlibs "$dir" $shared_libs
$plist_checks && for _s in $multi_packages; do
check_run_depends "$dir" "$_s" "${check_flavors[@]}"
done
! $error
} || error=true
if [ -z $pkgpath ] && ! $rootrun; then
pkgpath=$(cd -- "$dir"; make "${make_args[@]}" show=PKGPATH
2>/dev/null) ||
pkgpath=
fi
}
# Checks made:
# * Every library in SHARED_LIBS has 0.0 version.
check_shlibs() {
$debugging && echo "CALLED: check_shlibs($*)" >&2
local dir=$1; shift
local lib
local libver
local portref=$(portref "$dir")
while (($# > 1)); do
lib=$1
libver=$2
if [[ $libver != 0.0 ]]; then
err "${portref}the $lib shared library has" \
"version $libver instead of 0.0"
fi
shift 2
done
}
# Checks made:
# * Distfiles with names starting by digit go into DIST_SUBDIR.
check_distfiles() {
$debugging && echo "CALLED: check_distfiles($*)" >&2
local dir=$1; shift
local dist_subdir=$1; shift
local portref=$(portref "$dir")
# 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 "${portref}badly named distfile $1 without" \
"DIST_SUBDIR or {url} postfix"
fi
shift
done
}
# Checks made:
# * No unreliable (without fixed distfiles) hosting listed in MASTER_SITES.
check_master_sites() {
$debugging && echo "CALLED: check_master_sites($*)" >&2
local dir=$1; shift
local portref=$(portref "$dir")
local name
while (($# > 1)); do
case "$1" in
http?(s)://bitbucket.com/*) name=BitBucket;;
http?(s)://gitorious.com/*) name=Gitorious;;
*) name=;;
esac
[[ -n $name ]] && err "$portref$name does not hold real" \
"releases, please host the distfiles somewhere" \
"somewhere else or ask someone to do this for you"
shift
done
}
# Checks made:
# * If subpackage installs system-wide icons, it should have the
# x11/gtk+2,-guic dependency and @exec/@unexec-delete with
# %D/bin/gtk-update-icon-cache -q -t %D/share/icons/$theme
# for each icon theme used in package.
#
# * If subpackage adds a MIME type handler, it should have the
# devel/desktop-file-utils dependency and @exec/@unexec-delete with
# %D/bin/update-desktop-database
#
# * If subpackage adds a MIME types package, it should have the
# misc/shared-mime-info dependency and @exec/@unexec-delete with
# %D/bin/update-mime-database %D/share/mime
check_run_depends() {
$debugging && echo "CALLED: check_run_depends($*)" >&2
local dir=$1; shift
local subpkg=$1; shift
local flavor
for flavor in "$@"; do (
cd -- "$dir"
portref=$(portref "$dir" "$subpkg" "$flavor")
SUBPACKAGE="$subpkg" FLAVOR="$flavor" \
make "${make_args[@]}" print-plist-with-depends |
check_run_depends_main "$portref"
); done
}
check_run_depends_main() {
$debugging && echo "CALLED: check_run_depends_main($*)" >&2
local portref=$1; shift
local guic_dep=false
local guic_dep_needed=false
local guic_exec_cnt=0
local guic_unexec_cnt=0
local mime_dep=false
local mime_dep_needed=false
local mime_exec_cnt=0
local mime_unexec_cnt=0
local mimepkg_dep=false
local mimepkg_dep_needed=false
local mimepkg_exec_cnt=0
local mimepkg_unexec_cnt=0
local icon_themes exec_themes unexec_themes
local l theme varname
while read l; do
case "$l" in
share/icons/*/*)
guic_dep_needed=true
theme=${l#share/icons/}
theme=${theme%%/*}
# wrap with the '/' characters to avoid erroneous
matching
echo "$icon_themes" | fgrep -q "/$theme/" ||
icon_themes="$icon_themes /$theme/"
;;
"@depend x11/gtk+2,-guic"*)
guic_dep=true
;;
"@exec %D/bin/gtk-update-icon-cache -q -t %D/share/icons/"*)
theme=${l##*/}
varname=$(echo "$theme" | sed -e 's/[^a-zA-Z_]/_/g')
((guic_exec_cnt++)) || true
eval "((guic_exec_cnt_$varname++)) || true"
echo "$exec_icon_themes" | fgrep -q "/$theme/" ||
exec_icon_themes="$exec_icon_themes /$theme/"
;;
"@unexec-delete %D/bin/gtk-update-icon-cache -q -t
%D/share/icons/"*)
theme=${l##*/}
varname=$(echo "$theme" | sed -e 's/[^a-zA-Z_]/_/g')
((guic_unexec_cnt++)) || true
eval "((guic_unexec_cnt_$varname++)) || true"
echo "$unexec_icon_themes" | fgrep -q "/$theme/" ||
unexec_icon_themes="$unexec_icon_themes
/$theme/"
;;
"@unexec-delete rm -f "%D/share/icons/*/icon-theme.cache)
# as an alternative, port could zap the theme entirely
theme=${l#*/icons/}
theme=${theme%/icon-theme.cache}
varname=$(echo "$theme" | sed -e 's/[^a-zA-Z_]/_/g')
((guic_unexec_cnt++)) || true
eval "((guic_unexec_cnt_$varname++)) || true"
echo "$unexec_icon_themes" | fgrep -q "/$theme/" ||
unexec_icon_themes="$unexec_icon_themes
/$theme/"
;;
@?(un)exec?(-delete|-update)" %D/bin/gtk-update-icon-cache"*)
err "${portref}incorrect gtk-update-icon-cache" \
"invocation: ${l#@* }"
;;
share/applications/*(*/)*.desktop)
mime_dep_needed=true
;;
"@depend devel/desktop-file-utils"*)
mime_dep=true
;;
"@exec %D/bin/update-desktop-database")
((mime_exec_cnt++)) || true
;;
"@unexec-delete %D/bin/update-desktop-database")
((mime_unexec_cnt++)) || true
;;
@?(un)exec?(-delete|-update)" %D/bin/update-desktop-database"*)
err "${portref}incorrect update-desktop-database" \
"invocation: ${l#@* }"
;;
share/mime/packages/*.xml)
mimepkg_dep_needed=true
;;
"@depend misc/shared-mime-info"*)
mimepkg_dep=true
;;
"@exec %D/bin/update-mime-database %D/share/mime")
((mimepkg_exec_cnt++)) || true
;;
"@unexec-delete %D/bin/update-mime-database %D/share/mime")
((mimepkg_unexec_cnt++)) || true
;;
@?(un)exec?(-delete|-update)" %D/bin/update-mime-database"*)
err "${portref}incorrect update-mime-database" \
"invocation: ${l#@* }"
;;
esac
done
# gtk-update-icon-cache
if $guic_dep_needed && ! $guic_dep; then
err "${portref}missing RDEP on x11/gtk+2,-guic"
fi
local cnt
for theme in $icon_themes; do
theme=${theme#/}
theme=${theme%/}
varname=$(echo "$theme" | sed -e 's/[^a-zA-Z_]/_/g')
((guic_exec_cnt--)) || true
((guic_unexec_cnt--)) || true
eval "((guic_exec_cnt_$varname--)) || true"
eval "((guic_unexec_cnt_$varname--)) || true"
eval "cnt=\$guic_exec_cnt_$varname"
if (($cnt > 0)); then
err "${portref}extra @exec of gtk-update-icon-cache" \
"for icon theme $theme"
elif (($cnt < 0)); then
err "${portref}missing @exec of gtk-update-icon-cache" \
"for icon theme $theme"
fi
eval "cnt=\$guic_unexec_cnt_$varname"
if (($cnt > 0)); then
err "${portref}extra @unexec-delete of
gtk-update-icon-cache" \
"for icon theme $theme"
elif (($cnt < 0)); then
err "${portref}missing @unexec-delete of
gtk-update-icon-cache" \
"for icon theme $theme"
fi
done
for theme in $exec_icon_themes; do
theme=${theme#/}
theme=${theme%/}
echo "$icon_themes" | fgrep -q "/$theme/" ||
err "doing @exec of gtk-update-icon-cache" \
"for absent icon theme $theme"
done
for theme in $unexec_icon_themes; do
theme=${theme#/}
theme=${theme%/}
echo "$icon_themes" | fgrep -q "/$theme/" ||
err "doing @unexec-delete of gtk-update-icon-cache" \
"for absent icon theme $theme"
done
((guic_exec_cnt > 0)) &&
err "${portref}extra @exec of gtk-update-icon-cache"
((guic_unexec_cnt > 0)) &&
err "${portref}extra @unexec-delete of gtk-update-icon-cache"
# desktop-file-utils (simplier than previous, isn't it?)
$mime_dep_needed && ! $mime_dep &&
err "${portref}missing RDEP on devel/desktop-file-utils"
if $mime_dep_needed; then
((mime_exec_cnt--)) || true
((mime_unexec_cnt--)) || true
fi
if ((mime_exec_cnt > 0)); then
err "${portref}extra @exec of update-desktop-database"
elif ((mime_exec_cnt < 0)); then
err "${portref}missing @exec of update-desktop-database"
fi
if ((mime_unexec_cnt > 0)); then
err "${portref}extra @unexec-delete of update-desktop-database"
elif ((mime_unexec_cnt < 0)); then
err "${portref}missing @unexec-delete of
update-desktop-database"
fi
# update-mime-database (same as previous)
$mimepkg_dep_needed && ! $mimepkg_dep &&
err "${portref}missing RDEP on misc/shared-mime-info"
if $mimepkg_dep_needed; then
((mimepkg_exec_cnt--)) || true
((mimepkg_unexec_cnt--)) || true
fi
if ((mimepkg_exec_cnt > 0)); then
err "${portref}extra @exec of update-mime-database"
elif ((mimepkg_exec_cnt < 0)); then
err "${portref}missing @exec of update-mime-database"
fi
if ((mimepkg_unexec_cnt > 0)); then
err "${portref}extra @unexec-delete of update-mime-database"
elif ((mimepkg_unexec_cnt < 0)); then
err "${portref}missing @unexec-delete of update-mime-database"
fi
}
# Checks made:
# * Directory is not empty
# * No '*.core' files present
check_files_dir() {
$debugging && echo "CALLED: check_files_dir($*)" >&2
find -f "$1" -- -type f | {
local empty=true
while read F; do
ignoring "$F" && continue
empty=false
[[ "$F" == *.core ]] && err_coredump_found "$F"
done
$empty && "there are no files, please remove the $1 directory"
! $error
} || error=true
}
# Checks made:
# * Directory is not empty and consists only of plain files starting
# with 'patch-' and not ending with '.orig'.
check_patches_dir() {
$debugging && echo "CALLED: check_patches_dir($*)" >&2
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"
}
# Checks made:
# * Directory is not empty and consist only of plain files with fixed names.
check_pkg_dir() {
$debugging && echo "CALLED: check_pkg_dir($*)" >&2
local empty=true
local F
for F in "$1"/* "$1"/.*; do case "${F##*/}" in
DESCR?(-*)|MESSAGE?(-*)|PFRAG.*|PLIST?(-*)|README?(-*)|SECURITY?(-*)|UNMESSAGE?(-*)|*.rc)
if [[ -f $F ]]; then
empty=false
else
err "$F" is not a file
fi
;;
*)
handle_extra_file "$F"
;;
esac; done
$empty && err "$1 directory does not contain any DESCR or PLIST files"
}
# Checks made:
# * No REVISION marks present in given file.
check_makefile() {
$debugging && echo "CALLED: check_makefile($*)" >&2
$existing_port && return 0
grep -q '^ *REVISION' "$1" 2>/dev/null &&
err "REVISION(-s) found in $1"
}
# Checks made:
# * None of executable bits (111) are set on plain files.
check_perms_in_dir() {
$debugging && echo "CALLED: check_perms_in_dir($*)" >&2
find -f "$1" -- -maxdepth 1 -type f \
\( -perm -100 -or -perm -010 -or -perm 001 \) | {
while read F; do
F=${F#./}
ignoring "$F" && continue
err "executable file: ${F#./}"
done
! $error
} || error=true
}
############################################################
# Run checks and calculate pkgpath variable, that represents
# subdirectory under root ports directory where the port(-s)
# will be imported.
#
check_port_dir .
if ! $rootrun; then
[[ -z $pkgpath ]] && pkgpath=${PWD##"$portsdir/"}
if [[ $pkgpath == "$PWD" ]]; then
cat >&2 <<EOE
${0##*/}: could not determine PKGPATH. Please help me with the -p option.
EOE
exit 2
fi
echo "$pkgpath"
fi
! $error
<<< CUT HERE >>>
.\" $OpenBSD$
.\"
.\" 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
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate$
.Dt PORTCHECK 1
.Os
.Sh NAME
.Nm portcheck
.Nd validate a port before submitting
.Sh SYNOPSIS
.Nm
.Op Fl ACdPU
.Op Fl p Ar portsdir
.Op Fl x Ar pattern
.Sh DESCRIPTION
.Nm
is used to validate the
.Ox
port or port hierarchy in current directory.
It should be used before submitting ports for review to avoid doing
common mistakes.
.Nm
verifies that directory and file structure for a port is in place and
that no bogus files exist.
.Pp
When it's done,
.Nm
will print detected value of port's
.Ev PKGPATH
to standard output, unless it fails in detection.
In the latter case, the
.Fl p
option should be provided.
All other (error) messages from
.Nm
end up on standard error output.
.Pp
By default,
.Nm
automatically picks up nearest upper directory named
.Dq ports ,
with an optional
.Dq mystuff ,
.Dq openbsd-wip
or
.Dq p5-ports-wip
subdirectory component, as the ports root directory.
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 A
Intended for running
.Nm
on the whole ports tree, i.e., the one lying in
.Ev PORTSDIR .
This includes
adding a bunch of ignore patterns (see
.Fl x
description), implying
.Fl C
and
.Fl U
options and disabling further more checks (e.g., for missing distinfo).
Also,
.Ev
PKGPATH
determining and printing won't be done.
.It Fl C
Disables checks for the presence of CVS directories,
for checking ports already committed to CVS tree.
.It Fl d
Show debugging information such as calling of check routines.
.It Fl P
Disable expensive checks that use
.Dq print-plist-with-depends
target, e.g., proper usage of
.Xr gtk-update-icon-cache 1 ,
.Xr update-desktop-database 1
and
.Xr update-mime-database 1 .
.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
Disables checks like the presence of REVISION markers and non-0.0
.Ev SHARED_LIBS .
Intended to be used, e.g., when working on port updates.
.It Fl x
Excludes files and subdirectories matching given shell globbing pattern
from any checks.
Note that matching is done against relative path, and not against
absoulte path or base name either.
I.e., to exclude the
.Dq x11/kde4/libs/logs
from checks, you must pass the whole line as argument, not just
.Dq logs .
Multiple -x options may be specified.
.El
.Sh EXAMPLES
To validate a new port you've just prepared, go to port's directory and
run:
.Bd -literal -offset indent
$ portcheck
.Ed
.Pp
If you were working on updating of an existing port directly in CVS
tree, you can avoid extra noise by using the
.Fl C
and
.Fl U
options:
.Bd -literal -offset indent
$ portcheck -CU
.Ed
.Pp
To run a global check of the whole ports tree, use
.Fl a
option instead:
.Bd -literal -offset indent
$ cd /usr/ports && portcheck -a
.Ed
.Sh SEE ALSO
.Xr portimport 1
.Sh HISTORY
This utility was split off
.Xr portimport 1
in 2013 and first appeared in
.Ox 5.5 .