#!/bin/ksh # # $OpenBSD: portcheck,v 1.117 2017/12/22 22:59:53 zhuk 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 < 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 < SIGNING_PARAMETERS ?= EOF ) IFS=: set -A portsdir_path -- \ $( (echo "$test_mf" | 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 <&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 < 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 ;; 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 perm_pkg_cdrom perm_pkg_ftp perm_dist_ftp 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_CDROM PERMIT_PACKAGE_FTP" local show_items="$show_items PERMIT_DISTFILES_FTP" local read_ok=false local read_failed=false (cd -- "$dir"; make "${make_args[@]}" show="$show_items" || true) &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_CDROM${subpkg%-}" vars="$vars PERMIT_PACKAGE_FTP${subpkg%-}" make "${make_args[@]}" show="$vars" | { local comment fullpkgname modules pkg_arch local wantlib perm_pkg_cdrom perm_pkg_ftp read -r comment read -r fullpkgname read -r modules read -r pkg_arch read -r wantlib read -r wantlib_ss read -r perm_pkg_cdrom read -r perm_pkg_ftp if [[ $comment == @(a|an|the)" "* ]]; then err "${portref}no leading articles in" \ "COMMENT${subpkg%-}, please" fi if [[ $pkg_arch == "*" && -n $wantlib ]]; then if [[ $subpkg != - || -n $wantlib_ss ]]; then err "${portref}non-empty $wantlib_var for" \ "arch-independent package" fi fi check_wantlib "$portref" "$modules" $wantlib check_permit_subpkg "$portref" "$subpkg" \ "$perm_pkg_cdrom" "$perm_pkg_ftp" if $plist_checks; then (make "${make_args[@]}" \ print-plist-with-depends || true) \ /dev/null # 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 either x11/qt4,-main # or 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. # 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 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 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 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 dconf_exec_cnt=0 local dconf_unexec_cnt=0 # Lists of icon themes discovered through reading # @file, @exec and @unexec lines, accordingly. local icon_themes= exec_icon_themes= unexec_icon_themes= # List of icon themes that remove cache file local rm_cache_themes= 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 qt4_dep=false qt5_dep=false qt5declarative_dep=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. # # We match directories by purpose, this helps to catch # update-plist fuckups, when directories go into one # package and actual icons go in another. 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/" if [[ "$l" = "share/icons/$theme/index.theme" ]]; then echo "$rm_cache_themes" | fgrep -q "/$theme/" || err "${portref}missing @unexec-delete rm -f" \ "%D/share/icons/$theme/icon-theme.cache" fi ;; 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 ;; "@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)) eval "((++guic_exec_cnt_$varname))" 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)) eval "((++guic_unexec_cnt_$varname))" 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)) eval "((++guic_unexec_cnt_$varname))" unexec_icon_themes="$unexec_icon_themes /$theme/" rm_cache_themes="$rm_cache_themes /$theme/" if echo "$icon_themes" | fgrep -q "/$theme/"; then err "${portref}the @unexec-delete line removing" \ "%D/share/icons/$theme/icon-theme.cache" \ "does not preceed all of the icon theme" \ "$theme files" fi ;; @?(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)) ;; "@unexec-delete %D/bin/update-desktop-database") ((++mime_unexec_cnt)) ;; @?(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)) ;; "@unexec-delete %D/bin/update-mime-database %D/share/mime") ((++mimepkg_unexec_cnt)) ;; @?(un)exec?(-delete|-update)" %D/bin/update-mime-database"*) err "${portref}incorrect update-mime-database" \ "invocation: ${l#@* }" ;; share/glib-2.0/schemas/*.xml) dconf_dep_needed=true ;; "@depend devel/dconf"*) dconf_dep=true ;; "@exec %D/bin/glib-compile-schemas %D/share/glib-2.0/schemas >/dev/null") ((++dconf_exec_cnt)) ;; "@unexec-delete %D/bin/glib-compile-schemas %D/share/glib-2.0/schemas >/dev/null") ((++dconf_unexec_cnt)) ;; @?(un)exec?(-delete|-update)" %D/bin/glib-compile-schemas"*) err "${portref}incorrect glib-compile-schemas" \ "invocation: ${l#@* }" ;; "@depend x11/qt4,-main"*) qt4_dep=true ;; "@depend x11/qt5/qtdeclarative,-main"*) qt5declarative_dep=true qt5_dep=true ;; "@depend x11/qt5/"*) qt5_dep=true ;; lib/qt5/qml/*) qml_found=true qt5_qml_found=true ;; */qmldir) qml_found=true non_qt5_qml_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 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" ((guic_exec_cnt--)) || true 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" ((guic_unexec_cnt--)) || true 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 "${portref}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 "${portref}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 && [[ $fullpkgname != desktop-file-utils-* ]] && 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)) && [[ $fullpkgname != desktop-file-utils-* ]]; 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 && [[ $fullpkgname != shared-mime-info-* ]] && 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)) && [[ $fullpkgname != shared-mime-info-* ]]; 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 # 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 if $dconf_dep_needed; then ((dconf_exec_cnt--)) || true ((dconf_unexec_cnt--)) || true fi if ((dconf_exec_cnt > 0)) && [[ $fullpkgname != glib2-* ]]; then err "${portref}extra @exec of glib-compile-schemas" elif ((dconf_exec_cnt < 0)); then err "${portref}missing @exec of glib-compile-schemas" fi if ((dconf_unexec_cnt > 0)); then err "${portref}extra @unexec-delete of glib-compile-schemas" elif ((dconf_unexec_cnt < 0)); then err "${portref}missing @unexec-delete of glib-compile-schemas" fi # QML $qml_found && ! $qt4_dep && ! $qt5_dep && [[ $fullpkgname != qt4-[0-9]* ]] && [[ $fullpkgname != qtdeclarative-[0-9]* ]] && err "${portref}looks like providing QML files without" \ "Qt dependency" $qt5_qml_found && ! $qt5declarative_dep && [[ $fullpkgname != qtdeclarative-[0-9]* ]] && err "${portref}provides Qt5 QML files without" \ "x11/qt5/qtdeclarative dependency" $qt5_dep && ! $qt4_dep && $non_qt5_qml_found && err "${portref}depends on Qt5 but installs QML files" \ "outside PREFIX/lib/qt5/qml/" # 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/\${FULLPKGNAME} in PLIST${subpkg}" fi fi } # Checks made: # * lib/kde/ and lib/kde4/ prefixes not missing where applicable. # * stdc++ doesn't get into WANTLIB when gcc4.port.mk is used. check_wantlib() { local portref="$1"; shift local modules="$1"; shift local phonon_s_wantlib=false local kde3_module=false local kde4_module=false local phonon_module=false local gcc4_module=false local v for v in $modules; do case $v in gcc4) gcc4_module=true;; multimedia/phonon) phonon_module=true;; x11/kde) kde3_module=true;; x11/kde4) kde4_module=true;; esac; done for v; do case $v in phonon_s?(?(">")=+([0-9]))) phonon_s_wantlib=true ;; @(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!)" ;; @(kdecore|kdeui|kio)?(?('>')=+([0-9]))) if $kde4_module; then err "$portref$v instead of \${KDE4LIB}/$v" \ "in WANTLIB (check other libs, too!)" elif $kde3_module; then err "$portref$v instead of \${KDE}/$v" \ "in WANTLIB (check other libs, too!)" else err "$portref$v WANTLIB without x11/kde*" \ "in MODULES (check other libs, too!)" 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 if $phonon_s_wantlib && ! $phonon_module; then err "${portref}missing multimedia/phonon in MODULES" fi 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" <=]*} 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 make "${make_args[@]}" SUBDIR="${pure_lib_deps[*]}" \ print-plist-libs /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_FTP variables in Makefile. # * PERMIT_DISTFILES_FTP 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 perm_pkg_cdrom=$(echo "$1" | tr '[:upper:]' '[:lower:]') local perm_pkg_ftp=$(echo "$2" | tr '[:upper:]' '[:lower:]') local perm_dist_ftp=$(echo "$3" | tr '[:upper:]' '[:lower:]') if [[ ($perm_pkg_cdrom == yes || $perm_pkg_ftp == yes) && \ $perm_dist_ftp == yes ]]; then egrep -sq "^ *PERMIT_DISTFILES_FTP[[:space:]]*=" Makefile && err "${portref}extra PERMIT_DISTFILES_FTP line(-s)" fi if [[ $perm_dist_ftp == no ]]; then err "${portref}PERMIT_DISTFILES_FTP should be either" \ "\"Yes\" or a reason for being non-redistributable" fi true } # Checks made: # * No extra PERMIT_PACKAGE_FTP variables in Makefile. # * 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 perm_pkg_cdrom=$(echo "$1" | tr '[:upper:]' '[:lower:]') local perm_pkg_ftp=$(echo "$2" | tr '[:upper:]' '[:lower:]') if [[ $perm_pkg_cdrom == yes && $perm_pkg_ftp == yes ]]; then egrep -q "^ *PERMIT_PACKAGE_FTP${subpkg}[[:space:]]*=" Makefile && err "${portref}extra PERMIT_PACKAGE_FTP lines" fi if [[ $perm_pkg_cdrom == no ]]; then err "${portref} PERMIT_PACKAGE_CDROM should be either" \ "\"Yes\" or a reason for being non-redistributable" fi if [[ $perm_pkg_ftp == no ]]; then err "${portref} PERMIT_PACKAGE_FTP should be either" \ "\"Yes\" or a reason for being non-redistributable" fi true } # 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 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 [[ $F == *.core ]] && err_coredump_found "$F" done $empty && err "there are no files, please remove the $1 directory" ! $error } || error=true } # Checks made: # * The patch is not empty. # * The patch contains an OpenBSD RCS tag. check_patch() { local F=$1 test -f "$F" || { err "$F is not a file" return } if [ -s "$F" ]; then head -n 1 -- "$F" | egrep -q '^\$OpenBSD.*\$$' || err "$F does not have \$OpenBSD\$ RCS tag at the top" else err "$F is empty and should be removed" fi } # Checks made: # * Each patch contains OpenBSD RCS tag. # * 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. # * PFRAG, PLIST, README and .rc files contain appropriate OpenBSD RCS # tags; other files should NOT contain OpenBSD RCS tag. # * 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" egrep -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" head -n 1 -- "$F" | egrep -q '^(#[[:space:]]*)?\$OpenBSD(:.*)?\$$' || err "$F does not have \$OpenBSD\$ RCS tag at the top" ;; *.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##*/}" head -n 5 -- "$F" | egrep -q '^#[[:space:]]*\$OpenBSD(:.*)?\$$' || err "$F does not have \$OpenBSD\$ RCS tag at the top" ;; 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" egrep -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: # * There is an OpenBSD RCS tag at the top. # * 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" head -n 1 -- "$1" | egrep -q '^@comment \$OpenBSD.*\$$' || err "$1 does not have \$OpenBSD\$ RCS tag at the top" # 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 OpenBSD RCS tag at the top line. # * No REVISION marks present in given file (unless in update mode). # * Each REVISION mark presents only once. # * BUILD_DEPENDS, MODULES and PERMIT_DISTFILES_FTP 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 statemets. # * No user settings present. # * SHARED_ONLY not defined check_makefile() { $debugging && echo "CALLED: check_makefile($*)" >&2 local F="$1" check_trailing_whitespace "$F" check_long_lines "$F" check_hardcoded "$F" head -n 1 -- "$F" | egrep -q '^#[[:space:]]*\$OpenBSD.*\$' || err "$F does not have \$OpenBSD\$ RCS tag at the top" 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_FTP)-*) 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)" ;; 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 \) \ &2 <