ports/infrastructure/bin/portcheck
2024-07-29 19:08:43 +00:00

1923 lines
47 KiB
Bash
Executable file

#!/bin/ksh
#
# $OpenBSD: portcheck,v 1.147 2024/06/10 05:10:08 rsadowski 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= \
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
audio
converters
databases
devel
editors
fonts
geo
graphics
lang
mail
math
misc
multimedia
net
osint
print
security
shells
sysutils
telephony
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
SITE_BACKUP
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 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 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 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_sites "$dir" $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 SITES.
check_sites() {
$debugging && echo "CALLED: check_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+4,-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+4,-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" \
"RUN_DEPENDS${subpkg}+=x11/gtk+4,-guic"
local cnt
# desktop-file-utils (simplier than previous, isn't it?)
$mime_dep_needed && ! $mime_dep &&
[[ $fullpkgname != desktop-file-utils-* ]] &&
err "${portref}missing" \
"RUN_DEPENDS${subpkg}+=devel/desktop-file-utils"
# update-mime-database (same as previous)
$mimepkg_dep_needed && ! $mimepkg_dep &&
[[ $fullpkgname != shared-mime-info-* ]] &&
err "${portref}missing" \
"RUN_DEPENDS${subpkg}+=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_COMPILEALL}: $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