op 1b2ff04b83 portcheck: fix COMMENT check in the no MULTI_PACKAGE case
if MULTI_PACKAGE isn't set $subpkg is "-" and it breaks the check for
COMMENT.  Trim out the - with ${subpkg%-} as already done in other parts
of portcheck.

ok tb@, sthen@
2022-11-23 17:51:47 +00:00

1941 lines
47 KiB
Bash
Executable File

#!/bin/ksh
#
# $OpenBSD: portcheck,v 1.143 2022/11/23 17:51:47 op Exp $
# 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
set -u
usage() {
echo "usage: ${0##*/} [-dNP] [-p portsdir] [-x glob]" >&2
echo " ${0##*/} -A [-dP] [-p portsdir] [-x glob] [subdir ...]" >&2
exit 1
}
############################################################
# Parsing command line options
#
existing_port=true
ignore_cvs=true
plist_checks=true
portsdir=
rootrun=false
debugging=false
ignore_list=; unset ignore_list[0]
while getopts "AdNPp:x:" OPT; do
case $OPT in
A)
$existing_port || usage
if ! $rootrun; then
ignore_list[${#ignore_list[@]}]=.cvsignore
ignore_list[${#ignore_list[@]}]=.fslckout
ignore_list[${#ignore_list[@]}]=.git
ignore_list[${#ignore_list[@]}]=.gitignore
ignore_list[${#ignore_list[@]}]=.hg
ignore_list[${#ignore_list[@]}]=.hgignore
ignore_list[${#ignore_list[@]}]=.svn
ignore_list[${#ignore_list[@]}]=FINISHED
ignore_list[${#ignore_list[@]}]=INDEX
ignore_list[${#ignore_list[@]}]=README
ignore_list[${#ignore_list[@]}]=README.md
ignore_list[${#ignore_list[@]}]=bulk
ignore_list[${#ignore_list[@]}]=distfiles
ignore_list[${#ignore_list[@]}]=infrastructure
ignore_list[${#ignore_list[@]}]=lost+found
ignore_list[${#ignore_list[@]}]=mystuff
ignore_list[${#ignore_list[@]}]=openbsd-wip
ignore_list[${#ignore_list[@]}]=packages
ignore_list[${#ignore_list[@]}]=plist
ignore_list[${#ignore_list[@]}]=pobj
ignore_list[${#ignore_list[@]}]=tests
ignore_list[${#ignore_list[@]}]=update
fi
rootrun=true
;;
d)
debugging=true
;;
N)
$rootrun && usage
existing_port=false
ignore_cvs=false
;;
P)
plist_checks=false
;;
p)
portsdir=$OPTARG
;;
x)
set -A ignore_list -- "${ignore_list[@]}" "$OPTARG"
;;
*)
usage
;;
esac
done
if ! $rootrun && [[ -n $portsdir && ${PWD##"$portsdir"} == "$PWD" ]]; then
cat >&2 <<EOE
${0##*/}: current directory does not seem to be under the
specified root directory: $portsdir.
EOE
exit 3
fi
shift $(($OPTIND - 1))
(($# > 0)) && ! $rootrun && usage
(($# == 0)) && set -- .
############################################################
# 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
# idea from DPB/Vars.pm
test_mf=$(cat <<EOF
DUMMY_PACKAGE = yes
.include <bsd.port.mk>
EOF
)
IFS=:
set -A portsdir_path -- \
$( (echo "$test_mf" | env FLAVOR= make -C / -f - show=PORTSDIR_PATH 2>/dev/null) || true)
unset IFS
if ((${#portsdir_path[@]} > 0)); then
for p in "${portsdir_path[@]}"; do
if [[ -z $portsdir && ${PWD#"$p"} != "$PWD" ]]; then
portsdir=$p
elif [[ -n $portsdir && ${PWD#"$p"} != "$PWD" &&
$p > $portsdir ]]; then
portsdir=$p
fi
done
fi
fi
if [[ -z $portsdir ]]; then
# heuristics mode ON
pkgpath=${PWD##*/ports/*(mystuff/|openbsd-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 and/or
# when SKIPDIR framework is used
set -A make_args -- \
SKIPDIR= STARTAFTER= STARTDIR= \
MASTER_SITE_OPENBSD= \
PORTSDIR_PATH="$portsdir:$(cd /usr/ports && make -V PORTSDIR_PATH || true)"
if $rootrun; then
cd -- "$portsdir"
echo "scanning ports under the $portsdir" >&2
fi
############################################################
# Support for SKIPDIR, STARTDIR and STARTAFTER, see ports(7)
#
SKIPDIR=${SKIPDIR:-}
STARTDIR=${STARTDIR:-}
STARTAFTER=${STARTAFTER:-}
if [[ -n $STARTAFTER ]]; then
STARTDIR=$STARTAFTER
SKIPDIR="$SKIPDIR $STARTAFTER"
fi
path_parts_count() {
(IFS=/; set -- $1; echo $#)
}
# true if directory given should be skipped based on STARTDIR
# and/or SKIPDIR variable
skip_dir() {
$rootrun || return 1
local dir=$(readlink -f "$1")
dir=${dir##$portsdir*(/)}
local startpartscount=$(path_parts_count "$STARTDIR")
local dirpartscount=$(path_parts_count "$dir")
if ((dirpartscount >= startpartscount)); then
[[ -n $STARTDIR && $dir < $STARTDIR ]] && return 0
fi
local d
for d in $SKIPDIR; do
[[ $d == "$dir" ]] && return 0
done
return 1
}
############################################################
# List of well-known top-level port categories
# in a form usable in pattern matching: "foo|bar|baz"
#
top_level_categories=$(xargs <<EOF | sed -e 's/ /|/g'
archivers
astro
audio
base
benchmarks
biology
books
cad
chinese
comms
converters
databases
devel
editors
education
emulators
fonts
games
geo
graphics
inputmethods
japanese
java
korean
lang
mail
math
meta
misc
multimedia
net
news
perl5
plan9
print
productivity
security
shells
sysutils
telephony
tests
textproc
wayland
www
x11
EOF
)
############################################################
# List of variables that should not go into port Makefiles
#
user_settings=$(xargs <<EOF | sed -e 's/ /|/g'
BASELOCALSTATEDIR
BASESYSCONFDIR
BATCH
BUILD_ONCE
BULK
BULK_COOKIES_DIR
CHECKSUM_PACKAGES
CHECK_LIB_DEPENDS
COPTS
CXXOPTS
DISTDIR
ECHO_MSG
ECHO_REORDER
FAKEOBJDIR
FETCH_CMD
FETCH_PACKAGES
FETCH_SYMLINK_DISTFILES
FORCE_UPDATE
FTP_PACKAGES
IGNORE_IS_FATAL
IGNORE_SILENT
INTERACTIVE
LIST_DB
LOCKDIR
LOCK_VERBOSE
MASTER_SITE_BACKUP
MASTER_SITE_OVERRIDE
NO_CHECKSUM
NO_DEPENDS
NO_IGNORE
PACKAGE_REPOSITORY
PKG_ADD
PKG_CREATE
PKG_DBDIR
PKG_DELETE
PKG_INFO
REFETCH
SIGNING_PARAMETERS
SUDO
TEMPLATES
TRY_BROKEN
UNLOCK_CMD
UPDATE_COOKIES_DIR
USE_CCACHE
VARBASE
WARNINGS
EOF
)
############################################################
# Check and fail routines
#
error=false
err() {
local prefix=
while (($# > 0)); do
printf "$prefix%s" "$1" >&2
prefix=" "
shift
done
echo >&2
error=true
}
err_duplicated() {
err "both $2 and some of its parents has $1"
}
err_coredump_found() {
err "core dump file found: $1"
}
has_subdirs_only() {
$debugging && echo "CALLED: has_subdirs_only($*)" >&2
local dir=$1; shift
ls -A "$dir" | {
local has_files=false has_dirs=false
while read F; do
$ignore_cvs && [[ $F == CVS ]] && continue
ignoring "$dir/$F" && continue
if [[ -d $dir/$F ]]; then
has_dirs=true
else
has_files=true
fi
done
$has_dirs && ! $has_files
}
}
ignoring() {
((${#ignore_list[*]} > 0)) || return 1
local iglob
for iglob in "${ignore_list[@]}"; do
[[ ${1#./} == $iglob ]] && return 0
done
return 1
}
is_vcs_item() {
[[ -d "$1" && ${1##*/} == @(CVS|.fslckout|.git|.hg|.svn) ]]
}
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 [[ -f $1 && $1 == *.core ]]; then
err_coredump_found "$1"
elif [[ -d $1 ]]; then
err "extra directory: $1"
else
err "extra file: $1"
fi
}
# Make a path to .py[co] file looks like as if it's in the same dir
# as the corresponding .py file, and has same basename. E.g.:
# lib/python3.3/__pycache__/Foo/cpython-33.Bar.pyc
# became:
# lib/python2.7/Foo/Bar.pyc
# which corresponds to:
# lib/python2.7/Foo/Bar.py
normalize_pyco() {
local pyco=$1
[[ $pyco == *.cpython-+([0-9]).py[co] ]] &&
pyco=${pyco%.cpython-+([0-9]).py[co]}.${pyco##*.}
[[ $pyco == */__pycache__/* ]] &&
pyco=${pyco%/__pycache__/*}/${pyco##*/__pycache__/}
printf "%s" "$pyco"
}
# Print out a ref to the particular subport/subpackage, if needed.
# Port FLAVORs could also be handled, if provided.
# Usage: portref directory [subpackage [flavor all_flavors]]
portref() {
local dir=$1; shift
local subpkg= flavor all_flavors=
if (($# > 0)); then
subpkg=$1
shift
fi
if (($# > 0)); then
flavor=$1
all_flavors=$2
shift 2
fi
local ref=
if [[ $dir != . ]]; then
ref="${dir#./}"
[[ -n $subpkg && $subpkg != "-" ]] && ref="$ref,$subpkg"
else
[[ $subpkg != "-" ]] && ref="$subpkg"
fi
if [[ -n $all_flavors ]]; then
[[ -n $ref ]] && ref="$ref, "
if [[ -z $flavor ]]; then
ref="${ref}default FLAVOR"
else
ref="${ref}FLAVOR \"$flavor\""
fi
fi
[[ -n $ref ]] && echo "in $ref: "
}
# Contains last SUBST_CMD. Filled by check_port_dir(), used
# by check_port_hier() to lazily call the check_pkg_dir().
last_subst_cmd=
# Checks made:
# * Whitelist filter of what could be in this directory.
check_port_hier() {
$debugging && echo "CALLED: check_port_hier($*)" >&2
local distinfo_lives_upper pkg_lives_upper plist_lives_upper
local dir=$1; shift
for opt; do
# looks unsafe but we do not pass anything except
# "foo=true" and "foo=false" here
eval "$opt"
done
distinfo_lives_upper=${distinfo_lives_upper:-false}
pkg_lives_upper=${pkg_lives_upper:-false}
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 tell_pkg_exists=$pkg_lives_upper
if [[ -d $dir/pkg ]]; then
pkg_exists=true
tell_pkg_exists=true
fi
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
$plist_lives_upper && plist_exists=true
local recursive_args
set -A recursive_args -- \
distinfo_lives_upper=$distinfo_exists \
pkg_lives_upper=$tell_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)
check_${F##*/}_dir "$F"
;;
pkg)
# Do nothing, pkg_exists is already set,
# and we need to read SUBST_CMD first.
;;
patches?(-*))
check_patches_dir "$F"
;;
*)
if ! ([[ -f $F/Makefile ]] ||
ls $F/*.port.mk >/dev/null 2>&1) &&
! has_subdirs_only "$F"; 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
$pkg_exists && check_pkg_dir "$dir"/pkg "$last_subst_cmd"
$existing_port ||
egrep -q '^ *SUBDIR[[:space:]]*\+?=' "$dir"/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
skip_dir "$dir" && return
local distinfo_lives_upper pkg_lives_upper plist_lives_upper
for opt; do
# looks unsafe but we do not pass anything except
# "foo=true" and "foo=false" here
eval "$opt"
done
distinfo_lives_upper=${distinfo_lives_upper:-false}
pkg_lives_upper=${pkg_lives_upper:-false}
plist_lives_upper=${plist_lives_upper:-false}
check_perms_in_dir "$dir"
if [[ -f $dir/Makefile.inc ]] ||
egrep -sq '^ *SUBDIR[[:space:]]*\+?=' "$dir"/Makefile ||
has_subdirs_only "$dir"; 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))
;;
distinfo)
$distinfo_lives_upper && err_duplicated distinfo "$dir"
distinfo_exists=true
test -f "$F" || err "$F is not a file"
((++non_portmk))
;;
*.port.mk)
test -f "$F" || err "$F is not a file"
check_makefile "$F"
portmk_exists=true
;;
crates.inc|modules.inc)
test -f "$F" || err "$F is not a file"
fgrep -qx ".include \"$F\"" "$dir"/Makefile ||
err "$F not included in Makefile"
((++non_portmk))
;;
files|patches)
if [[ -d $F ]]; then
check_${F##*/}_dir "$F"
else
err "$F" is not a directory
fi
((++non_portmk))
;;
pkg)
if [[ -d $F ]]; then
pkg_exists=true
# Actual check to be done later, we need to gather
# additional info through "make show=" call.
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))
;;
*)
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_exists || $pkg_lives_upper || 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 categories dist_subdir distfiles flavor flavors
local gh_commit master_sites
local multi_packages pkgpath_this pseudo_flavor pseudo_flavors
local shared_libs subst_cmd
local permit_package permit_distfiles
local show_items="CATEGORIES DIST_SUBDIR DISTFILES FLAVOR FLAVORS GH_COMMIT"
local show_items="$show_items MASTER_SITES MULTI_PACKAGES PKGPATH"
local show_items="$show_items PSEUDO_FLAVOR PSEUDO_FLAVORS"
local show_items="$show_items SHARED_LIBS SUBST_CMD"
local show_items="$show_items PERMIT_PACKAGE PERMIT_DISTFILES"
local read_ok=false
local read_failed=false
(cd -- "$dir"; make "${make_args[@]}" show="$show_items" || true) </dev/null |&
read -pr categories &&
read -pr dist_subdir &&
read -pr distfiles &&
read -pr flavor &&
read -pr flavors &&
read -pr gh_commit &&
read -pr master_sites &&
read -pr multi_packages &&
read -pr pkgpath_this &&
read -pr pseudo_flavor &&
read -pr pseudo_flavors &&
read -pr shared_libs &&
read -pr subst_cmd &&
read -pr permit_package &&
read -pr permit_distfiles &&
read_ok=true
if $read_ok; then
exec 3<&p
exec 3<&-
wait
else
error=true
return
fi
pseudo_flavor=$(echo "$pseudo_flavor" | sed -e 's/,/ /g')
pseudo_flavor=${pseudo_flavor##" "}
local f pf found
local check_flavors=
[[ $flavor != "$pseudo_flavor" ]] && unset check_flavors[0]
for f in $flavors; do
for pf in $pseudo_flavors; do
[[ $f == "$pf" ]] && continue 2
done
[[ $f == debug ]] && continue # XXX
check_flavors[${#check_flavors[@]}]=$f
done
check_categories "$dir" $categories
check_distfiles "$dir" "$dist_subdir" $distfiles
check_master_sites "$dir" $master_sites
check_permit_dist "$dir" "$permit_package" "$permit_distfiles"
$pkg_exists && check_pkg_dir "$dir"/pkg "$subst_cmd"
$existing_port || check_shlibs_versions "$dir" $shared_libs
if [[ -n $gh_commit ]]; then
local ghclen=$(echo -n "$gh_commit" | wc -c)
if ((ghclen != 40)); then
err "GH_COMMIT should be in full form (40 characters)"
fi
fi
for _s in $multi_packages; do
sub_checks "$dir" "$_s" "${check_flavors[@]}"
done
pkgpath=${pkgpath:-"$pkgpath_this"}
last_subst_cmd="$subst_cmd"
}
# Checks made: obvious
check_dos_line_endings() {
grep -q "$(printf '\r\n')\$" "$1" &&
err "MS-DOS line endings in $1 -- if patching,"\
"use FIX_CRLF_FILES to correct the input file."
}
# Checks made: obvious
check_trailing_whitespace() {
egrep -q '[[:space:]]+$' "$1" &&
err "trailing whitespace in $1"
}
# Checks made: obvious
check_newline_at_eof() {
(( $(tail -1 -- "$1" | wc -l) == 0)) &&
err "no newline at EOF in $1"
}
# Checks made:
# * Every library in SHARED_LIBS has 0.0 version.
check_shlibs_versions() {
$debugging && echo "CALLED: check_shlibs_versions($*)" >&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:
# * All top-level category names are well-known.
check_categories() {
$debugging && echo "CALLED: check_categories($*)" >&2
local dir=$1; shift
local portref=$(portref "$dir")
for c in "$@"; do
c=${c%%/*}
if ! echo "$c" | egrep -q "^($top_level_categories)\$"; then
err "${portref}non-standard top-level category: $c"
fi
done
}
# Checks made:
# * Distfiles with useless names go into DIST_SUBDIR or have {url} suffix.
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
# try to catch "version-only" names, but not anything more
if [[ $1 == ?(v)?(.)+([0-9])?(.+([0-9]))*(.+([a-z])) &&
-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" \
"else or ask someone to do this for you"
shift
done
}
# Run checks that are FLAVOR/SUBPACKAGE-dependent.
sub_checks() {
$debugging && echo "CALLED: sub_checks($*)" >&2
local dir=$1; shift
local subpkg=$1; shift
local flavor
for flavor in "$@"; do
# avoid extra noise
[[ ${flavor#no_} != ${flavor} &&
${subpkg#-} == ${flavor#no_} ]] &&
continue
(
cd -- "$dir"
portref=$(portref "$dir" "$subpkg" "$flavor" "$*")
export SUBPACKAGE="$subpkg" FLAVOR="$flavor"
local wantlib_var=WANTLIB${subpkg%-}
local vars="COMMENT${subpkg%-} FULLPKGNAME${subpkg%-}"
vars="$vars MODULES"
vars="$vars PKG_ARCH$subpkg $wantlib_var WANTLIB-"
vars="$vars PERMIT_PACKAGE${subpkg%-}"
vars="$vars COMPILER"
make "${make_args[@]}" show="$vars" | {
local comment fullpkgname modules pkg_arch
local wantlib permit_package
local arch_independent=false
local default_compiler=false
read -r comment
read -r fullpkgname
read -r modules
read -r pkg_arch
[[ $pkg_arch == "*" ]] && arch_independent=true
read -r wantlib
read -r wantlib_ss
read -r permit_package
read -r compiler
if [[ $comment == @(a|an|the)" "* ]]; then
err "${portref}no leading articles in" \
"COMMENT${subpkg%-}, please"
fi
if $arch_independent && [[ -n $wantlib ]]; then
if [[ $subpkg != - || -n $wantlib_ss ]]; then
err "${portref}non-empty $wantlib_var for" \
"arch-independent package"
fi
fi
[[ "$compiler" == "base-clang base-gcc gcc3" ]] \
&& default_compiler=true
check_wantlib "$portref" "$modules" \
"$default_compiler" $wantlib
check_permit_subpkg "$portref" "$subpkg" \
"$permit_package"
if $plist_checks; then
(make "${make_args[@]}" \
print-plist-with-depends || true) \
</dev/null |&
check_plist "$portref" "$fullpkgname" \
"$flavor" "${subpkg%-}" "$modules" \
"$arch_independent"
check_lib_depends "$portref" "$subpkg" \
"$modules" "$wantlib"
wait
fi
! $error
} || error=true
! $error
) || error=true
done
wait
}
# Checks made:
# * If package installs system-wide icons, it should have the
# x11/gtk+3,-guic dependency and
# @tag gtk-update-icon-cache %D/share/icons/$theme
# for each icon theme used in package.
#
# * If package adds a MIME type handler, it should have the
# devel/desktop-file-utils dependency and @tag update-desktop-database .
# Unfortunately, it's hard to tell if there is a MIME type handler
# in .desktop file, so we just trigger if any .desktop files are added to
# ${PREFIX}/share/applications/ .
#
# * If package adds a MIME types package, it should have the
# misc/shared-mime-info dependency and @tag update-mime-database
#
# * If package adds a GLib schema, it should have
# @tag glib-compile-schemas
# and "devel/dconf" in MODULES (or at least RDEP on devel/dconf).
#
# * If package installs files under ${PREFIX}/share/dbus-1/system-services/,
# it must have a run-time dependency on x11/dbus,-suid.
#
# * Each .py should have corresponding .pyc files, to avoid
# generation of the latter at run-time.
#
# * Manual (man and info) pages should go under ${PREFIX}/{man,info},
# not under ${PREFIX}/share/{man,info}.
#
# * Manual pages shouldn't be compressed.
#
# * If port installs QML files, it should depend on
# x11/qt5/qtdeclarative,-main
#
# * Qt5-QML files should live under PREFIX/lib/qt5/qml/.
#
# * Qt5-QML-enabled port depend on x11/qt5/qtdeclarative.
#
# * @rcscript items and pkg-readmes have @mode, @owner and @group reset.
#
# * PKG_ARCH is not set to * if a library is present.
#
check_plist() {
$debugging && echo "CALLED: check_plist($*)" >&2
local portref=$1; shift
local fullpkgname=$1; shift
local flavor_list=$1; shift
local subpkg=$1; shift
local modules_list=$1; shift
local arch_independent=$1; shift
local flavor is_static=false
for flavor in $flavor_list; do
[[ $flavor == static ]] && is_static=true
done
local guic_dep=false
local guic_dep_needed=false
local mime_dep=false
local mime_dep_needed=false
local mime_tag_cnt=0
local mimepkg_dep=false
local mimepkg_dep_needed=false
local dconf_module
if [[ $modules_list == ?(* )devel/dconf?( *) ]]; then
dconf_module=true
else
dconf_module=false
fi
local dconf_dep=false
local dconf_dep_needed=false
local dbus_suid_dep=false is_dbus_suid=false
local dbus_suid_dep_needed=false
# Lists of .py, .pyc and .pyo items found, accordingly
local py_files= pyc_files= pyo_files=
unset py_files[0] pyc_files[0] pyo_files[0]
local wrong_man=false wrong_info=false
local regsh=false unregsh=false
local regsh_exec=false unregsh_exec=false
local qml_found=false qt5_qml_found=false non_qt5_qml_found=false
local qt5_dep=false qt5declarative_dep=false
local lib_found=false
local owner_set=false group_set=false mode_set=false
local readme_seen=false
# Temporary ones
local app l theme varname py
while read -pr l; do
case $l in
"@comment "*)
# ignore
;;
share/icons/*/*/*|share/icons/*/@(index.theme|iconrc?(-png)))
# Themes have at least two levels in depth.
#
guic_dep_needed=true
;;
share/icons/*(*/))
# Do not match intermediate directories to avoid false
# positives.
;;
share/icons/*.xpm)
app=${l#share/icons/}
app=${app%%/*}
app=${app%%.*}
err "${portref}installs icon ${l##*/} in ${l%/*}, it" \
"should likely go in share/pixmaps/ instead"
;;
share/icons/default.*)
;;
share/icons/*)
app=${l#share/icons/}
app=${app%%/*}
app=${app%%.*}
err "${portref}installs icon ${l##*/} in ${l%/*}, it" \
"should go in share/$app/icons/ or like instead"
;;
"@depend x11/gtk+3,-guic"*)
guic_dep=true
;;
@?(un)exec*" %D/bin/gtk-update-icon-cache"*)
err "${portref}obsolete gtk-update-icon-cache" \
"invocation: ${l#@* }"
;;
share/applications/*(*/)*.desktop)
mime_dep_needed=true
;;
"@depend devel/desktop-file-utils"*)
mime_dep=true
;;
@?(un)exec*" %D/bin/update-desktop-database"*)
err "${portref}obsolete update-desktop-database" \
"invocation: ${l#@* }"
;;
share/mime/packages/*.xml)
mimepkg_dep_needed=true
;;
"@depend misc/shared-mime-info"*)
mimepkg_dep=true
;;
@?(un)exec*" %D/bin/update-mime-database"*)
err "${portref}obsolete update-mime-database" \
"invocation: ${l#@* }"
;;
share/glib-2.0/schemas/*.xml)
dconf_dep_needed=true
;;
"@depend devel/dconf"*)
dconf_dep=true
;;
@?(un)exec*" %D/bin/glib-compile-schemas"*)
err "${portref}obsolete glib-compile-schemas" \
"invocation: ${l#@* }"
;;
"@depend x11/qt5/qtdeclarative,-main"*)
qt5declarative_dep=true
qt5_dep=true
;;
"@depend x11/qt5/"*)
qt5_dep=true
;;
lib/qt5/qml/*)
qml_found=true
lib_found=true
qt5_qml_found=true
;;
*/qmldir)
qml_found=true
non_qt5_qml_found=true
;;
*.so|*.a|*.so.*)
lib_found=true
;;
share/dbus-1/system-services/*)
dbus_suid_dep_needed=true
;;
"@depend x11/dbus,-suid"*)
dbus_suid_dep=true
;;
"@bin libexec/dbus-daemon-launch-helper")
is_dbus_suid=true
;;
lib/ghc/*/register.sh)
regsh=true
;;
lib/ghc/*/unregister.sh)
unregsh=true
;;
"@exec /usr/bin/env HOME=/nonexistent "%D/lib/ghc/*/register.sh" -v0")
regsh_exec=true
;;
"@unexec /usr/bin/env HOME=/nonexistent "%D/lib/ghc/*/unregister.sh" -v0 --force")
unregsh_exec=true
if $unregsh; then
err "${portref}unregister.sh call placed before" \
"script itself in PLIST${subpkg}"
fi
;;
# XXX KSH arrays are limited to 10239 items
share/@(doc|*(*/)examples)+(/*).py|?(s)bin/*.py)
# ignore
;;
*.py)
py_files[${#py_files[@]}]=$l
;;
*.pyc)
pyc_files[${#pyc_files[@]}]=$(normalize_pyco "$l")
;;
*.pyo)
pyo_files[${#pyo_files[@]}]=$(normalize_pyco "$l")
;;
share/man/*)
wrong_man=true
;;
share/info/*)
wrong_info=true
;;
"@man "*.gz)
err "${portref}compressed $l"
;;
@owner)
owner_set=false
;;
@group)
group_set=false
;;
@mode)
mode_set=false
;;
"@owner "*)
owner_set=true
;;
"@group "*)
group_set=true
;;
"@mode "*)
mode_set=true
;;
"@rcscript "*)
! $owner_set || err "${portref}$l has @owner set"
! $group_set || err "${portref}$l has @group set"
! $mode_set || err "${portref}$l has @mode set"
;;
share/doc/pkg-readmes/*)
! $owner_set || err "${portref}$l has @owner set"
! $group_set || err "${portref}$l has @group set"
! $mode_set || err "${portref}$l has @mode set"
readme_seen=true
;;
esac
done
# gtk-update-icon-cache
$guic_dep_needed && ! $guic_dep &&
[[ $fullpkgname != gtk-update-icon-cache-* ]] &&
err "${portref}missing RDEP on x11/gtk+3,-guic"
local cnt
# desktop-file-utils (simplier than previous, isn't it?)
$mime_dep_needed && ! $mime_dep &&
[[ $fullpkgname != desktop-file-utils-* ]] &&
err "${portref}missing RDEP on devel/desktop-file-utils"
# update-mime-database (same as previous)
$mimepkg_dep_needed && ! $mimepkg_dep &&
[[ $fullpkgname != shared-mime-info-* ]] &&
err "${portref}missing RDEP on misc/shared-mime-info"
# glib-compile-schemas (almost same as previous)
#
# TODO: detect situation of extra devel/dconf in MODULES
# (requires investigation of all subpackages).
if $dconf_dep_needed; then
if ! $dconf_module; then
err "${portref}GLib2 XML schemas found without" \
"devel/dconf in MODULES"
elif ! $dconf_dep; then
err "${portref}missing" \
"RUN_DEPENDS${subpkg}+=\${MODDCONF_RUN_DEPENDS}"
fi
fi
# QML
$qt5_qml_found && ! $qt5declarative_dep &&
[[ $fullpkgname != qtdeclarative-[0-9]* ]] &&
err "${portref}provides Qt5 QML files without" \
"x11/qt5/qtdeclarative dependency"
$qt5_dep && $non_qt5_qml_found &&
err "${portref}depends on Qt5 but installs QML files" \
"outside PREFIX/lib/qt5/qml/"
$lib_found && $arch_independent &&
err "${portref}arch-independent package contains library files"
# dbus,-suid
if $dbus_suid_dep_needed && ! $dbus_suid_dep && ! $is_dbus_suid; then
err "${portref}missing" \
"RUN_DEPENDS${subpkg}+=x11/dbus,-suid"
fi
# Haskell register/unregister
if $regsh && ! $regsh_exec; then
err "${portref}missing @exec of register.sh"
fi
if $unregsh && ! $unregsh_exec; then
err "${portref}missing @unexec of unregister.sh"
fi
# Python modules
((${#py_files[@]} > 0)) && set -sA py_files -- "${py_files[@]}"
((${#pyc_files[@]} > 0)) && set -sA pyc_files -- "${pyc_files[@]}"
((${#pyo_files[@]} > 0)) && set -sA pyo_files -- "${pyo_files[@]}"
local ic=0 io=0
if ((${#py_files[@]} > 0)); then for py in "${py_files[@]}"; do
while [[ $ic -lt ${#pyc_files[@]} ]]; do
[[ ${pyc_files[$ic]} < "$py"c ]] || break
# allowed behaviour
#err "${portref}compiled Python module without" \
# "source, expected: ${pyc_files[$ic]%c}"
((++ic))
done
if [[ $ic -lt ${#pyc_files[@]} &&
${pyc_files[$ic]} == "$py"c ]]; then
((++ic))
else
err "${portref}Python module without" \
"compiled version, consider using" \
"\${MODPY_BIN} \${MODPY_LIBDIR}/compileall.py: $py"
fi
while [[ $io -lt ${#pyo_files[@]} ]]; do
[[ ${pyo_files[$io]} < "$py"o ]] || break
# allowed behaviour
#err "${portref}optimized Python module without" \
# "source, expected: ${pyo_files[$io]%o}"
((++io))
done
if [[ $io -lt ${#pyo_files[@]} &&
${pyo_files[$io]} == "$py"o ]]; then
((++io))
# too much noise, maybe enable in the future
#else
# err "${portref}Python module without" \
# "optimized version: $py"
fi
done; fi
# allowed behaviour
#while (($ic < ${#pyc_files[@]})); do
# err "${portref}compiled Python module without source," \
# "expected: ${pyc_files[$ic]%c}"
# ((++ic))
#done
# allowed behaviour
#while (($io < ${#pyo_files[@]})); do
# err "${portref}optimized Python module without source," \
# "expected: ${pyo_files[$io]%o}"
# ((++io))
#done
$wrong_man && err "${portref}manual pages should go under" \
"\${PREFIX}/man/ rather than under \${PREFIX}/share/man/"
$wrong_info && err "${portref}info pages should go under" \
"\${PREFIX}/info/ rather than under \${PREFIX}/share/info/"
if ! $readme_seen; then
local readme=pkg/README${subpkg}
if [[ -e $readme ]]; then
err "${portref}missing share/doc/pkg-readmes/\${PKGSTEM} in PLIST${subpkg}"
fi
fi
}
# Checks made:
# * stdc++ doesn't get into WANTLIB when gcc4.port.mk is used.
check_wantlib() {
local portref="$1"; shift
local modules="$1"; shift
local default_compiler="$1"; shift
local gcc4_module=false
local v
for v in $modules; do case $v in
gcc4) gcc4_module=true;;
esac; done
for v; do case $v in
@(smbclient|wbclient)?(?('>')=+([0-9])))
err "$portref$v instead of lib/samba/$v" \
"in WANTLIB"
;;
@(DCOP|soundserver_idl|vcard)?(?('>')=+([0-9])))
err "$portref$v instead of \${KDE}/$v" \
"in WANTLIB (check other libs, too!)"
;;
@(c++|stdc++)?(?('>')=+([0-9])))
if $default_compiler; then
err "C++ libraries in WANTLIB with default COMPILER" \
"(most ports need 'COMPILER=base-clang ports-gcc'" \
"or 'COMPILER=base-clang ports-gcc base-gcc')"
fi
;;
stdc++?(?('>')=+([0-9])))
if $gcc4_module; then
err "$portref$v in WANTLIB when gcc4 is" \
"in MODULES; run port-lib-depends-check" \
"and if stdc++ is still there, check" \
"actual build thoroughly, it's broken"
fi
esac; done
true
}
# Checks made:
# * Each library mentioned in WANTLIB is accessible either:
# a) as a part of base system, in /usr/lib or /usr/X11R6/lib;
# b) via LIB_DEPENDS directly, or via deeper dependency of LIB_DEPENDS.
check_lib_depends() {
$debugging && echo "CALLED: check_lib_depends($*)" >&2
local portref="$1"; shift
local subpkg="$1"; shift
local modules="$1"; shift
local wantlib="$1"; shift
# The idea as follows: build full list of run-time dependencies, but
# without RUN_DEPENDS begin involved.
#
# Then we look at libs in each pkgpath we got, and strip those
# from WANTLIB. We also strip system libraries from /usr/lib
# and /usr/X11R6/lib. And all WANTLIBs coming from MODULES are stripped
# too, supposing that authors of those MODULES know what they're doing
# (without stripping 'em, we'll get many false positives).
#
# If there are any non-stripped items in WANTLIB, we found a problem.
#
# XXX those checks do not take actual versions into account!
# get list of all WANTLIBs coming from MODULES
local m modvars=
for m in $modules; do
m=${m##*/}
case $m in
python)
m=py
;;
esac
m=$(echo "MOD${m}_WANTLIB" | tr a-z A-Z)
modvars="$modvars $m"
done
local l modlibs=
make "${make_args[@]}" show="$modvars" </dev/null |&
while read -pr l; do
modlibs="$modlibs $l"
done
wait # make sure process exited before possible return below
# strip WANTLIBs coming from MODULES
local libsleft wl
for l in $modlibs; do
libsleft=
for wl in $wantlib; do
if [[ $l != "$wl" ]]; then
libsleft="$libsleft $wl"
elif $debugging; then
echo "WANTLIB ITEM $wl COMES FROM MODULES"
fi
done
[[ -n $libsleft ]] || return 0 # all libs found
wantlib=$libsleft
done
# prepare easy-to-use WANTLIB list in $checklibs
local wlprefix checklibs=
for wl in $wantlib; do
wl=${wl%%[><=]*}
wlprefix=${wl%/*}
[[ $wlprefix == "$wl" ]] && wlprefix=lib
wl=${wl##*/}
checklibs="$checklibs ${wlprefix}/lib$wl"
done
# strip system libraries
local d
for d in /usr /usr/X11R6; do
for l in $d/lib/lib*.@(so*(.+([0-9]))|a); do
libsleft=
for wl in $checklibs; do
if [[ $l != +(/*)/${wl}.@(so*(.+([0-9]))|a) ]]; then
libsleft="$libsleft $wl"
elif $debugging; then
echo "FOUND WANTLIB ITEM $wl: $l"
fi
done
[[ -n $libsleft ]] || return 0 # all libs found
checklibs=$libsleft
done
done
# get deep list of LDEPs
local lmake_args="${make_args[@]}"
lmake_args[${#lmake_args[@]}]="RUN_DEPENDS="
lmake_args[${#lmake_args[@]}]="RUN_DEPENDS$subpkg="
# Rely on the fact we're already in the port directory, see sub_checks().
# XXX ignoring make errors for now
local pure_lib_deps=$(make "${lmake_args[@]}" show-run-depends | sort)
[[ -n $pure_lib_deps ]] || return 0
# SUBDIR doesn't accept newline-separated values
set -A pure_lib_deps -- $pure_lib_deps
(
# strip libraries from ports
# TODO cache print-plist-libs output?
cd -- /usr/ports # XXX "$portsdir" fails for openbsd-wip and like
unset FLAVOR SUBPACKAGE
SUBDIR="${pure_lib_deps[*]}" make "${make_args[@]}" \
print-plist-libs </dev/null 2>/dev/null |&
while read -pr l; do
case $l in
"===> "*)
;;
*)
libsleft=
for wl in $checklibs; do
if [[ $l != +(/*)/${wl}.@(so*(.+([0-9]))|a) ]]; then
libsleft="$libsleft $wl"
elif $debugging; then
echo "FOUND WANTLIB ITEM $wl: $l"
fi
done
[[ -n $libsleft ]] || exit 0 # all libs found
checklibs=$libsleft
;;
esac
done
# prettify list of WANTLIBs left and print it
libsleft=
for wl in $checklibs; do
libsleft="$libsleft ${wl##*/lib}"
done
err "${portref}the following libraries in WANTLIB${subpkg%-}" \
"look like masked by RUN_DEPENDS${subpkg%-}:$libsleft"
wait
! $error
) || error=true
}
# Checks made:
# * No extra PERMIT_DISTFILES variables in Makefile.
# * PERMIT_DISTFILES should not contain just "No" but a reason.
#
# Runs in the port directory.
# XXX does not handle Makefile.inc and other .include cases correctly.
check_permit_dist() {
$debugging && echo "CALLED: check_permit_dist($*)" >&2
local portref=$(portref $1); shift
local permit_package=$(echo "$1" | tr '[:upper:]' '[:lower:]')
local permit_distfiles=$(echo "$2" | tr '[:upper:]' '[:lower:]')
if [[ $permit_package == yes && $permit_distfiles == yes ]]; then
egrep -sq "^ *PERMIT_DISTFILES[[:space:]]*=" Makefile &&
err "${portref}extra PERMIT_DISTFILES line(-s)"
fi
if [[ $permit_distfiles == no ]]; then
err "${portref}PERMIT_DISTFILES should be either" \
"\"Yes\" or a reason for being non-redistributable"
fi
true
}
# Checks made:
# * PERMIT_PACKAGE should not contain just "No" but a reason.
#
# Runs in the port directory.
# XXX does not handle Makefile.inc and other .include cases correctly.
check_permit_subpkg() {
$debugging && echo "CALLED: check_permit_subpkg($*)" >&2
local portref=$1; shift
local subpkg=${1%-}; shift
local permit_package=$(echo "$1" | tr '[:upper:]' '[:lower:]')
if [[ $permit_package == no ]]; then
err "${portref} PERMIT_PACKAGE should be either" \
"\"Yes\" or a reason for being non-redistributable"
fi
true
}
# Checks made:
# * Directory is not empty
# * No '*.core' files present
# * Files should not contain OpenBSD RCS tags
check_files_dir() {
$debugging && echo "CALLED: check_files_dir($*)" >&2
find -f "$1" -- -type f | {
local empty=true
local mode
while read F; do
ignoring "$F" && continue
mode=$(stat -f %p "$F" || true)
(( (0$mode & 0111) != 0 )) &&
err "executable file: $F"
empty=false
if [[ $F == *.core ]]; then
err_coredump_found "$F"
else
grep -q '\$OpenBSD.*\$' "$F" &&
err "$F should not contain \$OpenBSD\$ tag"
fi
done
$empty && err "there are no files, please remove the $1 directory"
! $error
} || error=true
}
# Checks made:
# * The patch is not empty.
# * The patch should not contain an OpenBSD RCS tag.
check_patch() {
local F=$1
test -f "$F" || {
err "$F is not a file"
return
}
if [[ -s $F ]]; then
grep -q '\$OpenBSD.*\$' "$F" &&
err "$F should not contain \$OpenBSD\$ tag"
else
err "$F is empty and should be removed"
fi
check_dos_line_endings "$F"
}
# Checks made:
# * Patches should not contain OpenBSD RCS tags.
# * 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
check_perms_in_dir "$1"
for F in "$1"/* "$1"/.*; do case ${F##*/} in
patch-*.orig)
handle_extra_file "$F"
;;
patch-*)
empty=false
$rootrun || check_patch "$F"
;;
*)
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.
# * Files should not contain OpenBSD RCS tags.
# * PFRAG.shared should be merged into PLIST.
# * No trailing whitespace for DESCR, MESSAGE, README, UNMESSAGE and
# .rc files (PLIST and PFRAG are better checked with "make package").
# * See also check_plist_file().
check_pkg_dir() {
$debugging && echo "CALLED: check_pkg_dir($*)" >&2
local dir=$1; shift
local subst_cmd
if (($# > 0)); then
# XXX should find the way to always obtain SUBST_CMD
subst_cmd=$1
shift
fi
local empty=true
local F
local plist
check_perms_in_dir "$dir"
dir="${dir#./}"
for F in "$dir"/* "$dir"/.*; do case ${F##*/} in
DESCR?(-*))
empty=false
[[ -f $F ]] ||
err "$F is not a file"
check_trailing_whitespace "$F"
check_newline_at_eof "$F"
check_long_lines "$F"
check_hardcoded "$F"
[[ -n $subst_cmd ]] && check_subst_vars "$F" "$subst_cmd"
grep -q '\$OpenBSD.*\$' "$F" &&
err "$F should not contain \$OpenBSD\$ tag"
;;
PFRAG.shared?(-*))
empty=false
[[ -n $subst_cmd ]] && check_subst_vars "$F" "$subst_cmd"
check_plist_file "$F"
plist=PLIST${F##*/PFRAG.+([!-])}
err "$F should be merged into $plist"
;;
PFRAG.*|PLIST?(-*))
empty=false
[[ -n $subst_cmd ]] && check_subst_vars "$F" "$subst_cmd"
check_plist_file "$F"
;;
README?(-*))
[[ -f $F ]] ||
err "$F is not a file"
[[ -n $subst_cmd ]] && check_subst_vars "$F" "$subst_cmd"
check_trailing_whitespace "$F"
check_newline_at_eof "$F"
check_long_lines "$F"
check_hardcoded "$F"
grep -q '\$OpenBSD.*\$' "$F" &&
err "$F should not contain \$OpenBSD\$ tag"
;;
*.rc)
[[ -f $F ]] ||
err "$F is not a file"
[[ ${F##*/} == [A-Za-z_]*([A-Za-z0-9_]).rc ]] ||
err "$F name will not work in rc.subr(8)"
check_trailing_whitespace "$F"
check_long_lines "$F"
check_hardcoded "$F"
check_rcscript "$dir" "${F##*/}"
grep -q '\$OpenBSD.*\$' "$F" &&
err "$F should not contain \$OpenBSD\$ tag"
;;
MESSAGE?(-*)|UNMESSAGE?(-*))
[[ -f $F ]] ||
err "$F is not a file"
[[ -n $subst_cmd ]] && check_subst_vars "$F" "$subst_cmd"
check_trailing_whitespace "$F"
check_newline_at_eof "$F"
check_long_lines "$F"
check_hardcoded "$F"
grep -q '\$OpenBSD.*\$' "$F" &&
err "$F should not contain \$OpenBSD\$ tag"
;;
*)
handle_extra_file "$F"
;;
esac; done
$empty && err "$dir directory does not contain either DESCR, PFRAG or PLIST files"
}
# Checks made:
# * There are no hardcoded /usr/local or /var paths in file.
# /var/log, /var/run and /var/tmp are perfectly fine, though.
check_hardcoded() {
$debugging && echo "CALLED: check_hardcoded($*)" >&2
perl -n -e 'BEGIN { $ec=0; }
if (m,/usr/local\b,o) { $ec=1; close ARGV; }
if (m,/var((?:/+[^/\s]+)*)(?:\s.*)?$,o) {
unless ($1 =~ m,^/+(?:log|run|tmp),o) {
$ec=1; close ARGV;
}
}
END { $? = $ec; }' \
"$1" || err "hardcoded paths detected in $1, consider using" \
"SUBST_VARS and TRUEPREFIX/LOCALBASE/LOCALSTATEDIR/VARBASE"
}
# Checks made:
# * All pkg/foo.rc files are found in some PLIST* or PFRAG*.
check_rcscript() {
$debugging && echo "CALLED: check_rcscript($*)" >&2
local dir="$1" rcname="$2"
fgrep -xsq "@rcscript \${RCDIR}/${rcname%.rc}" \
"$dir"/PLIST* "$dir"/PFRAG* || err \
"$F is not mentioned in any packing list"
}
# Checks made:
# * There are no lines longer than 80 characters that have at least
# one space (avoids warnings on long URLs etc.).
check_long_lines() {
$debugging && echo "CALLED: check_long_lines($*)" >&2
local file=$1; shift
local n=$(awk <"$file" \
'/[[:space:]]/ && length > 80 { n++ } END { print n+0 }')
(($n > 0 )) &&
err "$n line(s) longer than 80 chars in $file"
}
# Checks made:
# * Contains no OpenBSD RCS tag.
# * No items with ${FULLPKGNAME} are allowed, except readme.
# * No empty lines.
check_plist_file() {
$debugging && echo "CALLED: check_plist_file($*)" >&2
[[ -f $1 ]] ||
err "$1 is not a file"
grep -q '\$OpenBSD.*\$' "$1" &&
err "$1 should not contain \$OpenBSD\$ tag"
# Do not match just '${FULLPKGNAME}' because many ports use the
# following trick:
# @cwd ${LOCALBASE}/share/doc/pkg-readmes
# ${FULLPKGNAME}
egrep -v '^(share/doc/pkg-readmes/\$\{FULLPKGNAME\}|@comment .*)$' "$1" |
egrep '.\$\{FULLPKGNAME\}|\$\{FULLPKGNAME\}.' >&2 &&
err "$1 contains item(s) with \${FULLPKGNAME} in it, see above"
egrep -q '^[[:space:]]*$' "$1" && err "$1 contains empty lines"
}
# Checks made:
# * Every variable referenced by ${[A-Z]+} should be in ${SUBST_VARS}.
check_subst_vars() {
$debugging && echo "CALLED: check_subst_vars($*)" >&2
local F=$1; shift
local subst_cmd=$1; shift
# Add variables sometimes referenced in port docs.
eval "$subst_cmd" -DPATH=test -DWRKSRC=test <"$F" |
egrep '\$\{[A-Z]+\}' >&2 &&
err "looks like misspelled variables in $F, see above"
}
# Checks made:
# * Contains no OpenBSD RCS tag.
# * No REVISION marks present in given file (unless in update mode).
# * Each REVISION mark presents only once.
# * BUILD_DEPENDS, MODULES and PERMIT_DISTFILES are not defined in
# VAR-subpkg manner.
# * No trailing whitespace.
# * SHARED_LIBS are not defined inside ".if" statements.
# * Variables are not assigned via "=" twice outside of .if statements.
# * No user settings present.
# * SHARED_ONLY not defined
# * Check for usage of obsolete PERMIT_PACKAGE_* and PERMIT_DISTFILES_FTP
check_makefile() {
$debugging && echo "CALLED: check_makefile($*)" >&2
local F="$1"
check_trailing_whitespace "$F"
check_long_lines "$F"
check_hardcoded "$F"
grep -q '\$OpenBSD.*\$' "$F" &&
err "$F should not contain \$OpenBSD\$ tag"
local iflevel=0 l lnum=0 revs= t r mkvars= var duprevfound
# do not unset mkvars, having empty element(-s) is fine
unset revs[0]
local tab="$(print '\t')"
while IFS= read -r l; do ((++lnum))
set -A t -- $l
duprevfound=false
if echo "$l" | egrep -q "^ *($user_settings)[[:>:]].*\$"; then
err "user settings in port: $l"
fi
case $l in
*(" ")REVISION*)
$existing_port ||
err "REVISION mark found at $F:$lnum"
var=${t[0]%=}
if ((${#revs[@]} > 0)); then
for r in "${revs[@]}"; do
if [[ $var == "$r" ]]; then
err "duplicated $r in $F"
# avoid dup error messages
duprevfound=true
break
fi
done
fi
revs[${#revs[@]}]=${t[0]}
;;
*(" ")@(BUILD_DEPENDS|MODULES|PERMIT_DISTFILES)-*)
err "${l%%-*} is not a subpackageble variable, see $F:$lnum"
;;
*(" ").*(" "|"$tab")if*)
((++iflevel))
;;
*(" ").*(" "|"$tab")endif*)
((iflevel--))
;;
*(" ")SHARED_LIBS*(" "|"$tab")*(+|:|!)=*)
if ((iflevel > 0)); then
err "should not be inside .if block ($F:$lnum): $l"
fi
;;
*(" ")SHARED_ONLY*(" "|"$tab")*(+|:|!|\?)=*)
err "SHARED_ONLY is deprecated ($F:$lnum)"
;;
*(" ")PERMIT_PACKAGE_CDROM*(" "|"$tab")*(+|:|!|\?)=*)
err "PERMIT_PACKAGE_CDROM is deprecated," \
"use PERMIT_PACKAGE ($F:$lnum)"
;;
*(" ")PERMIT_PACKAGE_FTP*(" "|"$tab")*(+|:|!|\?)=*)
err "PERMIT_PACKAGE_FTP is deprecated," \
"use PERMIT_PACKAGE ($F:$lnum)"
;;
*(" ")PERMIT_DISTFILES_FTP*(" "|"$tab")*(+|:|!|\?)=*)
err "PERMIT_DISTFILES_FTP is deprecated," \
"use PERMIT_DISTFILES ($F:$lnum)"
;;
esac
if [[ $l == *(" ")+([A-Za-z0-9_-])*(" "|"$tab")?(\?)=* ]] &&
((iflevel == 0)) && ! $duprevfound; then
var=${t[0]%?(\?)=*}
for v in "${mkvars[@]}"; do
if [[ $v == "$var" ]]; then
err "duplicated assignment of $v" \
"at $F:$lnum"
break
fi
done
mkvars[${#mkvars[@]}]=$var
fi
done <"$F"
}
# 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 \) \
</dev/null || true) |&
local F
while read -pr F; do
F=${F#./}
ignoring "$F" && continue
err "executable file: ${F#./}"
done
}
############################################################
# Run checks. Also calculate and show pkgpath variable,
# unless we're checking the ports tree root dir.
#
for D; do
if [[ $D == /* ]]; then
err "absolute path $D ignored"
continue
fi
if [[ $D == *(*/)..*(/*) ]]; then
err "too many .. in $D, skipping"
continue
fi
check_port_dir "$D"
done
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