#!/bin/sh # xlint TEMPLATE - scan XBPS template for common mistakes export LC_ALL=C scan() { local rx="$1" msg="$2" grep -P -Hn -e "$rx" "$template" | sed "s/^\([^:]*:[^:]*:\)\(.*\)/\1 $msg/" } once() { head -n 1 } header() { if [ "$(head -n1 "$template")" != "# Template file for '$pkgname'" ]; then echo "$template:1: Header should be: # Template file for '$pkgname'" fi } exists_once() { for var in pkgname version revision short_desc maintainer license \ homepage; do case "$(grep -c "^${var}=" "$template")" in 0) echo "$template: '$var' missing!";; 1) ;; *) echo "$template: '$var' defined more than once";; esac done } variables_order() { local curr_index max_index max_index_line variables_end message line max_index=0 while IFS="" read -r line; do case "$line" in pkgname=*) curr_index=1;; reverts=*) curr_index=2;; version=*) curr_index=3;; revision=*) curr_index=4;; archs=*) curr_index=5;; wrksrc=*) curr_index=7;; create_wrksrc=*) curr_index=8;; build_wrksrc=*) curr_index=9;; only_for_archs=*) continue;; build_style=*) curr_index=11;; build_helper=*) curr_index=11;; cmake_args=*) curr_index=12;; cmake_builddir=*) curr_index=12;; configure_args=*) curr_index=12;; configure_script=*) curr_index=12;; go_build_tags=*) curr_index=12;; go_get=*) curr_index=12;; go_import_path=*) curr_index=12;; go_ldflags=*) curr_index=12;; go_package=*) curr_index=12;; go_mod_mode=*) curr_index=12;; make_build_args=*) curr_index=12;; make_build_target=*) curr_index=12;; make_check_args=*) curr_index=12;; make_check_target=*) curr_index=12;; make_cmd=*) curr_index=12;; make_install_args=*) curr_index=12;; make_install_target=*) curr_index=12;; make_use_env=*) curr_index=12;; meson_builddir=*) curr_index=12;; meson_cmd=*) curr_index=12;; meson_crossfile=*) curr_index=12;; perl_configure_dirs=*) curr_index=12;; pycompile_dirs=*) curr_index=12;; pycompile_module=*) curr_index=12;; python_versions=*) curr_index=12;; stackage=*) curr_index=12;; conf_files=*) continue;; make_dirs=*) continue;; hostmakedepends=*) curr_index=15;; makedepends=*) curr_index=16;; depends=*) curr_index=17;; checkdepends=*) curr_index=18;; short_desc=*) curr_index=19;; maintainer=*) curr_index=20;; license=*) curr_index=21;; homepage=*) curr_index=22;; changelog=*) curr_index=23;; distfiles=*) curr_index=24;; checksum=*) curr_index=25;; alternatives=*) curr_index=26;; fetch_cmd=*) curr_index=26;; bootstrap=*) continue;; "#"*) continue;; " "*) continue;; " "*) continue;; "_"*"="*) continue;; *"="*) curr_index=27;; "") variables_end=1;; *"{") variables_end=1;; *) continue;; esac if [ "$variables_end" ]; then break elif [ "$curr_index" -lt "$max_index" ]; then message="$template: Place $max_index_line= after ${line%%=*}=" elif [ "$curr_index" -gt "$max_index" ]; then max_index="$curr_index" max_index_line="${line%%=*}" if [ "$message" ]; then echo "$message" message= fi fi done < "$template" [ "$message" ] && echo "$message" } file_end() { if [ "$(tail -c 1 $template)" ]; then echo "$template:$(( $(wc -l < $template) + 1 )): File does not end with newline character" elif [ -z "$(tail -c 2 $template)" ]; then echo "$template:$(wc -l < $template): Last line is empty" fi } variables=$(echo -n "#.* _.* .*_descr .*_groups .*_homedir .*_pgroup .*_shell desc_option_.* AR AS CC CFLAGS CPP CPPFLAGS CXX CXXFLAGS GCC LD LDFLAGS LD_LIBRARY_PATH NM OBJCOPY OBJDUMP RANLIB READELF STRIP XBPS_FETCH_CMD allow_unknown_shlibs alternatives archs binfmts bootstrap broken build_options build_options_default build_style build_helper build_wrksrc changelog checkdepends checksum cmake_builddir conf_files configure_args configure_script conflicts create_wrksrc depends disable_parallel_build distfiles dkms_modules fetch_cmd font_dirs force_debug_pkgs go_build_tags go_get go_import_path go_ldflags go_package go_mod_mode homepage hostmakedepends keep_libtool_archives kernel_hooks_version lib32depends lib32disabled lib32files lib32mode lib32symlinks license maintainer make_build_args make_build_target make_check_args make_check_target make_cmd make_dirs make_install_args make_install_target make_use_env makedepends mutable_files nocross nodebug nopie noshlibprovides nostrip nostrip_files noverifyrdeps only_for_archs patch_args pkgname preserve provides pycompile_dirs pycompile_module pycompile_version python_version register_shell replaces repository restricted reverts revision run_depends sgml_catalogs sgml_entries shlib_provides shlib_requires short_desc skip_extraction skiprdeps stackage subpackages system_accounts system_groups tags triggers version wrksrc xml_catalogs xml_entries" | tr '\n' '|') ret=0 for template; do if [ -f "$template" ]; then exists_once "$template" scan 'short_desc=.*\."' "unwanted trailing dot in short_desc" scan 'short_desc=["'\''][a-z]' "short_desc should start uppercase" scan 'short_desc=["'\''](An?|The) ' "short_desc should not start with an article" scan 'short_desc=["'\''][\t ]' "short_desc should not start with whitespace" scan 'short_desc=["'\''].{73}' "short_desc should be less than 72 chars" scan 'license=.*[^NL]GPL[^-]' "license GPL without version" scan 'license=.*SSPL' "Uses the SSPL license, which is not packageable" scan 'license=.*LGPL[^-]' "license LGPL without version" if ! grep -q vlicense "$template"; then for l in custom AGPL MIT BSD ISC; do scan "license=.*$l" "license '$l', but no use of vlicense" done else if ! grep license= "$template" | grep -Pqv -e 'license="(\b(LGPL|GPL|GFDL|Apache)-\S+(, )?)+"'; then scan "vlicense" 'license '"$(grep -Po license='\K.+' "$template" | tr -d '",' | tr '\n' ' ')"'should not be installed' | once fi fi : "${LICENSE_LIST:=/usr/share/spdx/license.lst}" if [ -f "${LICENSE_LIST}" ]; then sed -n 's/license="\(.*\)"/\1/p' "$template" | tr , "\n" | while read -r l; do case "$l" in custom:*|'Public Domain') continue ;; esac if ! grep -q "^${l}$" "${LICENSE_LIST}"; then scan "license=.*$l" "use SPDX id for '$l' license or see Manual.md" fi done fi if ! sed -n '/^version=/{n;/revision=/b;q1}' "$template"; then scan 'revision=' "revision does not appear immediately after version" fi scan 'vinstall.* 0?755.*usr/bin' "use vbin" scan 'vinstall.* usr/share/man' "use vman" scan 'vinstall.* usr/share/licenses' "use vlicense" scan '^ ' "indent with tabs" | once scan '[\t ]$' "trailing whitespace" scan '[^\\]`' "use \$() instead of backticks" scan '^pkgname="[^$]+"' "pkgname must not be quoted" scan 'revision=0' "revision must not be zero" scan '^version=.*[:-].*' "version must not contain the characters : or -" scan '^version=.*\${.*[:!#%/^,@].*}.*' "version must not use shell variable substitution mechanism" scan '^version="[^$]+"' "version must not be quoted" scan '^reverts=.*-.*' "reverts must not contain package name" scan '^reverts=(?!.*_.*).*' "reverts without revision" scan 'replaces=(?=.*\w)[^<>]*$' "replaces needs depname with version" scan 'maintainer=(?!.*<.*@.*>).*' "maintainer needs email address" scan 'maintainer=.*<.*@users.noreply.github.com>.*' "maintainer needs a valid address for sending mail" scan 'nonfree=' "use repository=nonfree" scan '^(?!\s*('"$variables"'))[^\s=-]+=' \ "custom variables should use _ prefix: \2" scan '^[^ =]*=(""|''|)$' "variable set to empty string: \2" scan '^(.*)-docs_package().*' 'use -doc subpackage for documentation' scan 'distfiles=.*github.com.*/archive/.*\.zip[\"]?$' 'Use the distfile .tar.gz instead of .zip' scan 'distfiles=.*downloads\.sourceforge\.net' 'use $SOURCEFORGE_SITE' scan 'distfiles=.*savannah.nongnu\.org' 'use $NONGNU_SITE' scan 'distfiles=.*archive\.ubuntu\.com' 'use $UBUNTU_SITE' scan 'distfiles=.*x\.org/releases/individual' 'use $XORG_SITE' scan 'distfiles=.*ftp.*debian\.org' 'use $DEBIAN_SITE' scan 'distfiles=.*gnome\.org/pub' 'use $GNOME_SITE' scan 'distfiles=.*www\.kernel\.org/pub/linux' 'use $KERNEL_SITE' scan 'distfiles=.*cpan\.org/modules/by-module' 'use $CPAN_SITE' scan 'distfiles=.*files\.pythonhosted\.org/packages' 'use $PYPI_SITE' scan 'distfiles=.*ftp\.mozilla\.org' 'use $MOZILLA_SITE' scan 'distfiles=.*ftp\.gnu\.org/(pub/)?gnu' 'use $GNU_SITE' scan 'distfiles=.*freedesktop\.org/software' 'use $FREEDESKTOP_SITE' scan 'distfiles=.*download.kde.org/stable' 'use $KDE_SITE' scan 'distfiles=.*xorg\.freedesktop\.org/wiki/' 'use $XORG_HOME' scan 'usr/lib/python3.[0-9]/site-packages' 'use $py3_sitelib' scan '^wrksrc=(\$\{[^}]+\}|[^${}/])*/.+' 'wrksrc should be a top-level directory' scan '^\t*function\b' 'do not use the function keyword' scan '^\t*[^ ]* *\(\)' 'do not use space before function parenthesis' scan '^\t*[^ ]*\(\)(| *){' 'use one space after function parenthesis' scan '^\t*[^ ]*\(\)$' 'do not use a newline before function opening brace' pkgname=$(grep -Po "^pkgname=\K.*" "$template") version=$(grep -Po "^version=\K.*" "$template") scan "distfiles=.*\Q$version\E" 'use ${version} in distfiles instead' variables_order header file_end else echo no such template "$template" 1>&2 fi | sort -t: -n -k2 | grep . && ret=1 done exit $ret