diff --git a/LEGAL b/LEGAL deleted file mode 100644 index 65b4c4714f0d..000000000000 --- a/LEGAL +++ /dev/null @@ -1,226 +0,0 @@ -# Creator: Jordan Hubbard -# -# ********************************************************************** -# *** NOTE TO COMMITTERS *** -# *** *** -# *** If you are adding a port to this list that has previously been *** -# *** distributed, please inform portmgr@FreeBSD.org so that the *** -# *** distfile/packages can be removed from the FTP site. *** -# ********************************************************************** -# -# Some of the ports in this directory have restrictive copyrights prohibiting -# their actual redistribution on mass media. When creating compilations -# of FreeBSD on such mass media, you should be sure and NOT distribute -# the original source tarballs for such ports in your distfiles directory. -# To do so will invite needless problems from the various parties involved. -# -# Since those connected to the net will also be able to use the ports framework -# to automatically fetch their own original sources and thus complete the puzzle, -# the hardship is not as severe as it might otherwise be. -# -# Such ports are listed below: -# -# This file can be linted with Tools/scripts/LEGALlint -# -# Dist Port Why -# ------------------------------------------------------------------------------ -*-for-1.4.tgz net/dgd-lpmud May not be used for monetary gain -*_MegaCLI.zip sysutils/megacli Source recipient must acknowledge license. Reproduction or redistribution prohibited. See http://www.lsi.com/lookup/License.aspx -26410-800.zip audio/libaacplus unclear legal status, probably need licenses from 3GPP, Via Licensing and Coding Technologies -aacplusenc-* audio/aacplusenc unclear legal status, probably need licenses from 3GPP, Via Licensing and Coding Technologies -aestats-* games/aestats Commercial use is prohibited -agrep-* textproc/agrep No redistribution for profit -alephone/M1A1.zip games/alephone-data Bungie/Microsoft do not allow charging for Marathon data files -alephone/Marathon*.zip games/alephone-data Bungie/Microsoft do not allow charging for Marathon data files -apache2/mod_h264_streaming-* www/mod_h264_streaming No commercial use -astah-community-*.zip devel/astah-community License restrictions -- See http://astah.net/faq/community/enduser-license-agreement -atari800/* emulators/atari800 xf25 contains copyright ROMs and cannot be distributed -baudline_* audio/baudline No redistribution allowed -BCWipe-* security/bcwipe Non-commercial distribution and use only -bicom* archivers/bicom Contact author personally regarding commercial use -Browser*.tar.gz net/ldapbrowser No reply from author regarding redistribution licensing -citrix_ica-* net/citrix_ica License prohibits redistribution -arcconf-v* sysutils/arcconf May not be redistributed in binary form -cloudflare-cloudflared* net/cloudflared Not sure if we are allowed to redistribute it. -clustalw-* biology/clustalw No commercial use -cos-* java/cos Special conditions apply for commercial use -CrystalSVGforGnomeIcons* x11-themes/gnome-icons-crystal Themes may contain artwork not done by the author. Keep FreeBSD safe if theme author violated copyrights. -cyberbit/* x11-fonts/cyberbit-ttfonts Not free for commercial use -dbvis java/dbvis Redistribution is not permitted -dcl-*-C.tar.gz science/cdcl No commercial use -dcl-*.tar.gz science/dcl No commercial use -djbdns* dns/djbdns No license -- see http://cr.yp.to/softwarelaw.html -djbfft-* math/djbfft No license -- see http://cr.yp.to/softwarelaw.html -dlv lang/dlv Not sure if we can redistribute it -dn*.tgz emulators/darcnes Commercial use is restricted -doom3-linux-* games/linux-doom3 Redistribution is limited, see license; Requested by id Software -doom3-linux-* games/linux-doom3-demo Redistribution is limited, see license; Requested by id Software -doomsrc.zip games/doom Redistribution is limited, see license; Requested by id Software -dr_freebsd_*.zip sysutils/megarc Must be downloaded directly from the manufacturer -eagle-lin-*.run cad/linux-eagle5 Usage permitted for non-commercial purposes only -eawpats* audio/eawpats Some of the patches are not for commercial use -ecw_jpeg_2000_*.zip graphics/libecwj2 Registration required to download sources, but sources no longer available on ERDAS website -edith* editors/edith Redistribution not allowed -eijiro* japanese/eijiro-fpw The original dictionary is not free -emc2* math/emc2 No resale, contact author for commercial usage -emiclock-* x11-clocks/emiclock Changes on the code or repackaging requires author approval -et-linux-* games/linux-enemyterritory Redistribution limited -etqw* games/linux-etqw-demo-server Redistribution limited -etqw* games/linux-etqw-server Redistribution limited -ezmlm-* mail/ezmlm No license -- see http://cr.yp.to/softwarelaw.html -ezmlm-* mail/ezmlm-idx No license -- see http://cr.yp.to/softwarelaw.html -fahclient-* biology/linux-foldingathome Distribution without permission is prohibited -fasta2* biology/fasta May not be sold or incorporated into a commercial product -fasta3* biology/fasta3 May not be sold or incorporated into a commercial product -festival/festlex_OALD* audio/festlex-oald No commercial use -festival/OGIlexicon-* audio/festlex-ogi No commercial use -festlex-ogi audio/festlex-ogi No commercial use -fmsx*/* emulators/fmsx Legal status of distributed ROM images unclear -foiltex* textproc/foiltex Redistribution on a not-for-profit basis only -fp-freebsd-ws-* security/f-prot Free for personal users on personal workstations only -fuse-emulator-roms* emulators/fuse-roms No explicit permission to redistribute -Opus-22.rom emulators/fuse-roms No explicit permission to redistribute -freefonts-* x11-fonts/freefonts License prohibits redistribution; see individual .license files -fretsonfire/FretsOnFire-*-linux* games/fretsonfire-data Only Unreal Voodoo site is allowed to distribute the music -fvcom/sourcecode1.tar.gz science/fvcom Requires (free) registration -fvcom/sourcecode1.tar.gz science/fvcom-mpi Requires (free) registration -giac-* math/giacxcas The french documentation is for non commercial only use -giftool* graphics/giftool Do not redistribute for profit -gnome2/mcitymicrogui-default* x11-themes/metacity-theme-microgui Themes may contain artwork not done by the author. Keep FreeBSD safe if the theme author violated copyrights -GoogleEarthLinux-* astro/google-earth Not really sure about the redistribution terms -groupoffice-com-* www/groupoffice Free for personal use only -HeroesOfMightAndMagic-* games/HeroesOfMightAndMagic Software is commercial -homard-* french/homard The free downloading of HOMARD software is only allowed for coupling with Code_Aster. For any other use, a license agreement is requested. Please contact HOMARD project: . -hplip-*-plugin.run print/hplip-plugin License agreement is required -HyperSpec-* devel/clisp-hyperspec Commercial redistribution prohibited -HyperSpec-*.tar.gz devel/clisp-hyperspec Commercial redistribution prohibited -ICON-AquaFusion.* x11-themes/gnome-icons-aqua-fusion Themes may contain artwork not done by the author. Keep FreeBSD safe if theme author violated copyrights. -ICON-Gentoo-Test.* x11-themes/gnome-icons-gentoo-test Themes may contain artwork not done by the author. Keep FreeBSD safe if theme author violated copyrights. -ICON-Gnome-RH8.* x11-themes/gnome-icons-refined Themes may contain artwork not done by the author. Keep FreeBSD safe if theme author violated copyrights. -ICON-Iris* x11-themes/gnome-icons-iris Themes may contain artwork not done by the author. Keep FreeBSD safe if theme author violated copyrights. -ICON-Noia.* x11-themes/gnome-icons-noia-full Themes may contain artwork not done by the author. Keep FreeBSD safe if theme author violated copyrights. -ICON-NoiaWarm.* x11-themes/gnome-icons-noia-warm Themes may contain artwork not done by the author. Keep FreeBSD safe if theme author violated copyrights. -ICON-Slick.* x11-themes/gnome-icons-slick Themes may contain artwork not done by the author. Keep FreeBSD safe if theme author violated copyrights. -ICON-Snow-Apple.* x11-themes/gnome-icons-snow-apple Themes may contain artwork not done by the author. Keep FreeBSD safe if theme author violated copyrights. -ICON-Stylish.* x11-themes/gnome-icons-stylish Themes may contain artwork not done by the author. Keep FreeBSD safe if theme author violated copyrights. -ICON-Ximian-South-* x11-themes/gnome-icons-ximian-south Themes may contain artwork not done by the author. Keep FreeBSD safe if theme author violated copyrights. -jai-*-lib-linux-i586.* java/jai Redistribution is not permitted -java3d-sdk-*-linux-i386* java/java3d License does not allow distribution -jdk-8*-apidocs.zip java/jdk8-doc This documentation is under license and export control -jdk-8*-i586.tar.gz java/linux-oracle-jdk18 Redistribution of repackaged binaries not permitted -jfbuild-* games/jfsw Must be distributed only through the Internet and free of charge -jmf-*-alljava.zip java/jmf Redistribution of pre-compiled binaries is not permitted -jre-8*-i586.tar.gz java/linux-oracle-jre18 License does not allow distribution with fee -jta-*.zip java/jta See the license -komodo-* editors/komodo-edit Distribution not permitted -kzip-* archivers/kzip License does not allow commercial redistribution -l0phtcrack/* security/l0phtcrack Parts are under GPL, so binaries should not be distributed. Contains crypto code from OpenSSL -ladder.tar games/ladder Possible copyright problems -lha-1.14i-ac* archivers/lha-ac No redistribution allowed -libaacplus-* audio/libaacplus Unclear legal status -libamrnb/26104-*.zip audio/libamrnb unclear legal status, probably need licenses from 3GPP and more -libamrwb/26204-*.zip audio/libamrwb unclear legal status, probably need licenses from 3GPP and more -libdvdcss-*.tar.bz2 multimedia/libdvdcss CSS code may violate the DMCA -linux-nwnclient/* games/linux-nwnclient Distribution not allowed -linux-rkbin/* sysutils/linux-rkbin Unknown license -linux-unrealgold* games/linux-unrealgold Redistribution limited -linux-ut/* games/linux-ut Redistribution is limited, see license -linuxq3ademo-* games/linux-quake3 Restrictive license by Loki Software -linuxq3ademo-* games/linux-quake3-demo Restrictive license by Loki Software -linuxsampler-* audio/linuxsampler Commercial use is prohibited -Logitech-slimserver-* audio/logitechmediaserver Contains non-redistributable firmware, documentation, and images -lpac-* audio/lpac No commercial use -l_cc_p*_*.*.*.tar.gz lang/icc No redistribution allowed -macopix-*.tar.gz games/macopix Contains commercial character data -madfufw-* audio/madfufw No redistribution of firmware files -makemkv-bin-* multimedia/makemkv Do not sell. Do not redistribute modified binaries, but explicit permission to distribute a patched makemkvcon given by copyright owner -malo-fw-*.tar.gz net/malo-firmware-kmod Marvell refuses to grant distribution rights -mb339pan_* games/flightgear-mb339-pan Redistribution prohibited -mindfocus-* games/mindfocus Contains commercial character data -mindterm-* security/mindterm-binary Do not sell for profit -mist64-cbmbasic-* lang/cbmbasic Based on decompiled C64 KERNAL binary; not really sure about the legal status -molden* biology/molden Free for academic, non-profit usage; do not redistribute source and executable -nero2_linux_* games/linux-nerogame Freeware, but license terms are unclear -NetComponents-* java/netcomponents No redistribution except as part of a substantially different product -nntpcache-* news/nntpcache Free for individuals and non-military, non-profit organisations only -nttcp-* benchmarks/nttcp No commercial use -nwresources*.tar.gz games/nwndata Distribution not allowed -nyan--xmascot-* x11/xmascot Commercial use is prohibited -oneko-*.tar.gz games/oneko-sakura Including derivatives from CARDCAPTOR SAKURA -OptimFROG* audio/optimfrog No commercial use -oracle/* databases/jdbc-oracle11g This software is under license and export controls -oracle/sqldeveloper* databases/sqldeveloper This software is under license and export controls -oracle/instantclient-basic-linux* databases/linux-oracle-instantclient-basic Packaging prohibited by Oracle license -pcsv*fb.tar.gz lang/petite-chez User must accept license terms before installation -PDFlib-Lite-*.tar.gz print/pdflib Many odd restrictions on usage and distribution -ppsmall-* archivers/ppunpack No commercial redistribution -qtools-* mail/qtools No license -- see http://cr.yp.to/softwarelaw.html -quake4-linux-* games/linux-quake4 Redistribution is limited; see the license -quake4-linux-* games/linux-quake4-demo Redistribution is limited; see the license -Radiator/* net/radiator Commercial software -RakNet-* devel/raknet Special authorization granted to distribute version 3.x under GPLv3 -rapid-* math/rapid For any commercial purpose, you must request a license from http://www.cs.unc.edu/~geom/OBB/OBBT.html -rarbsd-* archivers/rar only unmodified original package can be distributed -ricty-* japanese/font-ricty Redistribution not allowed -ringtonetools-* misc/ringtonetools Not free for commercial organizations -seatools_cli.tar sysutils/seatools Redistribution is not permitted -serialmail-* mail/serialmail No license -- see http://cr.yp.to/softwarelaw.html -serious.sam* games/linux-ssamtfe Redistribution limited -serious.sam* games/linux-ssamtse Redistribution limited -sharefonts-* x11-fonts/sharefonts Shareware: cannot be distributed without prior authorization -simian-* devel/simian Have to agree to license before download -STonX-* emulators/stonx Contains ROM (C) by Atari -stuffit* archivers/stuffit Only unmodified original package can be distributed -susv2.tar.bz2 misc/susv2 Redistribution of the documents is not permitted -susv3.tar.bz2 misc/susv3 Redistribution of the documents is not permitted -susv4tc2.tar.bz2 misc/susv4 Redistribution of the documents is not permitted -susv4-2018.tar.bz2 misc/susv4 Redistribution of the documents is not permitted -svm_light.tar.gz science/svmlight The software must not be further distributed without prior permission of the author -taetgen-* math/tetgen Academic or personal use only -tarsnap-* sysutils/tarsnap license restricts redistribution of source and binaries -tcetest_* games/linux-enemyterritory-tce Redistribution limited -teamspeak* audio/teamspeak3-server No redistribution -thundercache www/thundercache Commercial software with shareware license to 50 threads -triangle-*/* math/triangle No commercial use without prior arrangement with the author -Tripwire-*-1.tar.gz security/tripwire-131 Cannot be redistributed for more than the cost of duplication -TrueCrypt* security/truecrypt May not be redistributed. Must accept license to download. -tuc-* converters/tuc Author does not allow CD-ROM distribution -tw_cli-* sysutils/tw_cli Redistribution forbidden by license -ue*dev.zip editors/uemacs Resale allowed for media cost only -unifi-5*/UniFi.unix.zip net-mgmt/unifi5 Redistribution of bundled firmware images is not permitted -userApps.v*.src.tgz biology/ucsc-userapps Redistribution is limited, see license -uzap.tar.gz editors/uzap This software may not be used to make a profit in any way. -vdbench*.zip benchmarks/linux-vdbench Redistribution is not permitted -vdr-markad-* multimedia/vdr-plugin-markad Installs station logos -vice-* emulators/vice ROMs are copyrighted by Commodore Business Machines -VMware-vSphere-Perl* net/vmware-vsphere-cli May not be redistributed. Must accept license to download. -vpnc-* security/vpnc Redistribution is not allowed if linked against OpenSSL -vwnc7.3.1/* lang/visualworks Do not redistribute -waeijiro-fpw-* japanese/waeijiro-fpw The original dictionary is not free -webcopy-* www/webcopy This program cannot be distributed if modified in any way. Don't sell for profit without written permission -webfonts/* x11-fonts/webfonts Restrictive copyright -win32/* multimedia/win32-codecs Licensing status of the codecs is unknown -xanim-* multimedia/xanim Must be redistributed without fee -xchada01.lzh games/xchadance Contains commercial character data -xephem-* astro/xephem Personal use only -XFrisk-*.tar.gz games/xfrisk Possible trademark infringement -xhime*.tgz games/xhime Contains commercial character data -xinvaders-* games/xinvaders Do not sell for profit -xmdiary-* deskutils/xmdiary Do not sell for profit -xrisk-* games/xrisk Possible copyright/trademark violation -xtr1.50e* japanese/xtr Need to obtain permission for commercial redistribution -xzx-pro-*.tar.gz emulators/xzx Redistribution forbidden by license -yEd* graphics/yed Downloading requires acceptance of license agreement -zh-moettf/* chinese/moettf Contact Taiwan's Ministry of Education for commercial use -UrbanTerror*.zip games/urbanterror-data The mod files may not be sold or distributed on physical media unless with permission from id Software. -gmsh-* cad/gmsh Contact appropriate authors for commercial purposes if including Tetgen or Metis -sas2ircu-* sysutils/sas2ircu May not be redistributed. Must accept license to download. -snes9x-* emulators/snes9x-gtk Commercial users must seek permission from copyright holders. -rainloop-1.*.zip mail/rainloop Redistribution in any form is prohibited -lha-114i.tar.gz archivers/lha Binary only redistribution is prohibited. Non-network redistribution such as CDROM requires prior notification to author. Commercial use whose main purpose is to provide the functions of this program is prohibited. -charm-uiuc-* net/charm No commercial use: commercial use requires a commercial license -namd-* science/namd No commercial use: commercial use requires a commercial license -vmd-* science/vmd No commercial use: commercial use requires a commercial license -msms-* science/msms No commercial use, only for use by individuals in scientific research -netio* benchmarks/netio No commercial use: commercial use requires a commercial license -zerotier-* net/zerotier No SaaS or Govt usage: https://github.com/zerotier/ZeroTierOne/blob/master/LICENSE.txt diff --git a/Tools/scripts/LEGALlint b/Tools/scripts/LEGALlint deleted file mode 100755 index d75ade6cc0b6..000000000000 --- a/Tools/scripts/LEGALlint +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -PORTSDIR="${PORTSDIR:-/usr/ports}" - -cd "$PORTSDIR" || exit 1 - -export IFS="${IFS}:" -grep -nv '#' "$PORTSDIR/LEGAL" | while read -r line _ port text -do - if [ ! -d "$port" ] - then - printf "%d (%s): port has been removed\\n" "$line" "$port" - continue - fi - - actual_text="$(make -C "$port" -VLEGAL)" - if [ "$text" != "$actual_text" ] - then - printf "%d (%s): reason mismatch\\n" "$line" "$port" - fi -done diff --git a/Tools/scripts/README b/Tools/scripts/README index a14f94ab6eeb..1b7d2b838270 100644 --- a/Tools/scripts/README +++ b/Tools/scripts/README @@ -1,131 +1,130 @@ NOTE: These scripts need work and are *NOT* safe to use unless you know what they do. Use at your own risk. Patches would be great, but it is preferred they pass through the maintainer of each particular script. MOVEDlint.awk - checks MOVED for common errors -LEGALlint - checks LEGAL for common errors ardiff - compare two archives easily addport - replacement for easy-import bad-pkgdescrs.sh - locate identical pkg descriptions bump_revision.pl - Small script to bump the PORTREVISION variable of ports which are depending on a port with a changed shared lib version. checkcats.py - verify that master categories in all ports are correct and report any problems. Beware that the full check takes quite some time. checknewvers - checks for availability for a newest version of distfiles on MASTER_SITES (ftp only). checksum - allows checking of ports to see if their checksums match, and if they don't, give a diff against the older version to help discover why the checksum didn't match. chkorigin.sh - checks all ports in the tree for a wrong PKGORIGIN. Run this tool after every repocopy. doportlint - run portlint on every port and return the results distclean - compare md5 sums of distfiles in ports/distfiles with currently installed ports collection in ports/* and prompt to remove unmatched entries getpatch - downloads patch attachments from a Bug Tracking Systems getpatch.sh - downloads patch attachments from a Bug Tracking Systems (plain shell script) gnomedepends - Analyse pkg/PLIST and give an advice as to which GNOME ports should be listes in {RUN,LIB}_DEPENDS for this port mark_safe.pl - utility to set subsets of ports to MAKE_JOBS_(UN)SAFE=yes neededlibs.sh - Extract direct library dependencies from binaries. portsearch - A utility for searching the ports tree. It allows more detailed search criteria than ``make search key='' and accepts all perl(1) regular expressions. search_lib_depends_and_bump.sh - Give it a port that has changed and it will bump all ports having a LIB_DEPENDS on this port splitpatch.pl - A small script to convert multi-file patches to several appropriately named single-file patches. tindex - script used to build INDEXes for supported FreeBSD branches, which are the source of the 'make fetchindex' INDEXes, and the build failure reports on ports@FreeBSD.org update-patches - generates updated patches. ---------------------------------------------------------------------- gnomedepends is a script, which analyses pkg/PLIST and gives an advice as to which GNOME ports should be listes in {RUN,LIB}_DEPENDS for the port to ensure correct removal of GNOME shared directories. Usage is simple: % cd /usr/ports/CATEGORY/PORT % gnomedepends.py According to the contents of PLIST the port depends on the following GNOME port(s): /usr/ports/audio/gnomeaudio, for directories: share/gnome/sounds /usr/ports/sysutils/gnomecontrolcenter, for directories: share/gnome/apps /usr/ports/x11/gnomecore, for directories: share/gnome/apps/Games /usr/ports/x11/gnomelibs, for directories: etc/sound/events etc/sound share/gnome/games share/gnome/pixmaps share/gnome The example above means that you need to have ${PORTSDIR}/audio/gnomeaudio, ${PORTSDIR}/sysutils/gnomecontrolcenter, ${PORTSDIR}/x11/gnomecore and ${PORTSDIR}/x11/gnomelibs listed in {RUN,LIB}_DEPENDS for this port. Please be warned, that the this only means that the ports listed by the script required for correct removal of GNOME shared directories, not for the port functionality, so actual {RUN,LIB}_DEPENDS may have more entries. ---------------------------------------------------------------------- portsearch - A utility for searching the ports tree. portsearch is a utility to for searching of the ports tree. It permits much more detailed searches to be performed than ``make search key='' by allowing you to specify which field(s) to search. It also supports all valid perl(1) regular expressions for pattern matching. portsearch displays matching ports in the same format as ``make search'' and also displays the number of matching ports found. The following command line options are supported: -h Prints a multi-line help message and exits -n name Search for name in the name field -p path Search for path in the path field -i info Search for info in the comments field -m maint Search for maint in the Maintainer field -x index Search for index in the category field -b b_deps Search for b_deps in the build-depends field -r r_deps Search for r_deps in the run-depends field -d deps Search for deps in the both the build and run dependency fields. This option behaves differently to the other op- tions, see the EXAMPLES section -f file Use the index file instead of /usr/ports/INDEX All searches are case-insensitive See the file README.portsearch for further information. ---------------------------------------------------------------------- The update-patches script looks for files in $WRKSRC (if unset, this defaults to the work/ subdirectory of the current directory) which have a matching .orig file. It also looks in $PATCHDIR (if unset, this defaults to the files/ subdirectory of the current directory) for patches that correspond to the first set. If the changes in an existing patch do not reflect the changes in the files in $WRKSRC, the script renames the existing patch by adding the suffix .orig and generates a new patch in its place. If no patch existed, the new one is created with a name that contains the path and filename of the file being patched, except that "/" separators and "." characters are replaced by underscores: for example, a new patch to $WRKSRC/foo/bar.c would be created as $PATCHDIR/patch-foo_bar_c. If you save a .orig backup of a file, but don't change the file, update-patches will generate an empty patch. diff --git a/Tools/scripts/rmport b/Tools/scripts/rmport index 016bc59c902b..2a96de034335 100755 --- a/Tools/scripts/rmport +++ b/Tools/scripts/rmport @@ -1,494 +1,468 @@ #!/bin/sh -e # # rmport - remove port(s) from the FreeBSD Ports Collection. # # Copyright 2006-2007 Vasil Dimov # Copyright 2012-2018 Chris Rees # Copyright 2016-2021 René Ladan # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # Authors: # Originally written by Vasil Dimov # Others: # Chris Rees # René Ladan # # MAINTAINER= crees@FreeBSD.org # EDITOR=${EDITOR:-/usr/bin/vi} PORTSDIR=${PORTSDIR:-/usr/ports} INDEX=${PORTSDIR}/$(make -C ${PORTSDIR} -V INDEXFILE) TODAY=$(date -u +%Y-%m-%d) SED="sed -i .orig -E" # use ~/.ssh/config to set up the desired username if different than $LOGNAME GITREPO=${GITREPO:-git@gitrepo.FreeBSD.org:ports.git} if [ -n "$(command -v git 2>/dev/null)" ]; then GIT=git else echo "git(1) not found. Please install devel/git." exit 66 fi log() { echo "==> $*" >&2 } escape() { # escape characters that may appear in ports' names and # break regular expressions echo "${1}" |sed -E 's/(\+|\.)/\\\1/g' } pkgname() { make -C ${PORTSDIR}/${1} -V PKGNAME } ask() { question=${1} answer=x while [ "${answer}" != "y" ] && [ "${answer}" != "n" ] ; do read -p "${question}? [yn]" answer done echo ${answer} } # return category/port if arg is directly port's directory on the filesystem find_catport() { arg=${1} if [ -d "${PORTSDIR}/${arg}" ] ; then # arg is category/port echo ${arg} elif [ -d "${arg}" ] ; then # arg is the port's directory somewhere in the filesystem # either absolute or relative # get the full path rp=$(realpath ${arg}) category=$(basename $(dirname ${rp})) port=$(basename ${rp}) echo ${category}/${port} else echo "What do you mean by '${arg}'?" >&2 exit 64 fi } find_expired() { for category in $(make -C ${PORTSDIR} -V SUBDIR); do for port in $(make -C ${PORTSDIR}/${category} -V SUBDIR); do DATE="$(make -C ${PORTSDIR}/${category}/${port} -V EXPIRATION_DATE)" # shellcheck disable=SC2039 if [ -n "${DATE}" ] && [ ! "${DATE}" \> "$${TODAY}" ] ; then if [ "$1" = 1 ] ; then echo -n "${DATE} ${category}/${port}: " make -C ${PORTSDIR}/${category}/${port} -V DEPRECATED else echo "${category}/${port}" fi fi done done } # check if some ports depend on the given port # XXX Very Little Chance (tm) for breaking INDEX exists: # /usr/ports/INDEX may be outdated and not contain recently added dependencies check_dep_core() { catport=${1} alltorm=${2} pkgname=$(pkgname ${catport}) rmpkgs="" rmcatports="" for torm in ${alltorm} ; do torm="$(echo ${torm} | sed 's/\/$//')" rmpkgs="${rmpkgs:+${rmpkgs}|}$(pkgname ${torm})" rmcatports="${rmcatports:+${rmcatports}|}${PORTSDIR}/${torm}/" done err=0 deps=$(grep -E "${pkgname}" ${INDEX} |grep -vE "^(${rmpkgs})" || :) if [ -n "${deps}" ] ; then log "${catport}: some port(s) depend on ${pkgname}:" echo "${deps}" >&2 err=1 fi # check if some Makefiles mention the port to be deleted portdir_grep="^[^#].*/$(basename ${catport})([[:space:]]|@|/|$)" r="$(${GIT} grep '${portdir_grep}' -- '**Makefile*' 'Mk/' \ |grep -vE "^(${rmcatports})" || :)" if [ -n "${r}" ] ; then if [ ${err} -eq 1 ] ; then echo >&2 fi log "${catport}: some Makefiles mention ${portdir_grep}:" echo "${r}" >&2 err=1 fi return ${err} } check_dep() { catport=${1} persist=${2} alltorm=${3} log "${catport}: checking dependencies" err=0 res="$(check_dep_core ${catport} "${alltorm}" 2>&1)" || err=1 if [ ${err} -eq 0 ] ; then return 0 fi echo "${res}" |${PAGER:-less} if [ ${persist} -eq 0 ] ; then return 0 fi echo "" >&2 echo "you can skip ${catport} and continue with the rest or remove it anyway" >&2 answer=$(ask "do you want to skip ${catport}") if [ "${answer}" = "y" ] ; then return 1 else return 0 fi } # query Bugzilla and return the result get_PRs() { catport=${1} synopsis=${2} log "${catport}: getting PRs having ${synopsis} in the synopsis" url="https://bugs.freebsd.org/bugzilla/buglist.cgi?quicksearch=${synopsis}" raw="$(fetch -q -T 20 -o - "${url}")" if [ -z "${raw}" ] ; then log "${catport}: empty result from URL: ${url}" exit 67 fi printf "%s" "${raw}" \ |sed -ne 's,^[[:space:]]*.a href="show_bug.cgi?id=\([0-9][0-9]*\)".\([^0-9][^<]*\).*,\1: \2,p' \ |sort } # check if any PRs exist that are related to the port check_PRs() { catport=${1} synopsis=${2} PRs="$(get_PRs ${catport} "${synopsis}")" || exit if [ -n "${PRs}" ] ; then log "${catport}: PRs found, related to ${synopsis}:" printf "%s\n" "${PRs}" >&2 echo "you can skip ${catport} and continue with the rest or remove it anyway" >&2 answer=$(ask "do you want to skip ${catport}") if [ "${answer}" = "y" ] ; then return 1 else return 0 fi fi return 0 } -# check if anything about the port is mentioned in ports/LEGAL -check_LEGAL() -{ - catport=${1} - pkgname=${2} - - for checkstr in ${pkgname} ${catport} ; do - msg="${catport}: checking if ${checkstr} is in ports/LEGAL" - log "${msg}" - while grep -i ${checkstr} LEGAL ; do - echo "" >&2 - echo "${checkstr} is in ports/LEGAL" >&2 - echo "remove it and hit when ready" >&2 - echo "or hit 's' to skip this issue and continue anyway" >&2 - read answer - if [ "${answer}" = "s" ] ; then - break - fi - log "${msg}" - done - done - ${GIT} add LEGAL -} - # add port's entry to ports/MOVED edit_MOVED() { catport=${1} DEPRECATED="$(make -C ${PORTSDIR}/${catport} -V DEPRECATED)" DEPRECATED=${DEPRECATED:+: ${DEPRECATED}} if [ -n "$(make -C ${PORTSDIR}/${catport} -V EXPIRATION_DATE)" ] ; then REASON="Has expired${DEPRECATED}" else REASON="Removed${DEPRECATED}" fi log "${catport}: adding entry to ports/MOVED" echo "${catport}||${TODAY}|${REASON}" >> MOVED ${GIT} add MOVED } # remove port from category/Makefile edit_Makefile() { cat=${1} port=${2} log "${cat}/${port}: removing from ${cat}/Makefile" portesc=$(escape ${port}) ${SED} -e "/^[[:space:]]*SUBDIR[[:space:]]*\+=[[:space:]]*${portesc}([[:space:]]+#.*)?$/d" ${cat}/Makefile ${GIT} add ${cat}/Makefile } # remove port's files rm_port() { catport=${1} log "${catport}: scheduling port removal" echo ${catport} >> ${gitrmlist} } append_Template() { catport=${1} msg=${catport} EXPIRATION_DATE=$(make -C ${PORTSDIR}/${catport} -V EXPIRATION_DATE) if [ -n "${EXPIRATION_DATE}" ] ; then msg="${EXPIRATION_DATE} ${msg}" fi DEPRECATED="$(make -C ${PORTSDIR}/${catport} -V DEPRECATED)" if [ -n "${DEPRECATED}" ] ; then msg="${msg}: ${DEPRECATED}" fi log "${catport}: adding entry to commit message template" echo "${msg}" >> ${gitlog} } # update, ask for confirmation and make a commit commit() { ${GIT} commit --file=${gitlog} answer=$(ask "Do you want to tweak the commit message") if [ "${answer}" = "y" ] ; then ${GIT} pull --ff-only --rebase 2>&1 ${GIT} commit 2>&1 # modify final commit message echo "All done, check the result and push when everything is OK." fi } cleanup() { log "cleaning up" rm -f ${gitlog} ${gitrmlist} } usage() { echo "Usage:" >&2 echo "" >&2 echo "find expired ports:" >&2 echo "${0} -F" >&2 echo "" >&2 echo "remove port(s):" >&2 echo "${0} category1/port1 [ category2/port2 ... ]" >&2 echo "" >&2 echo "remove all expired ports (as returned by -F):" >&2 echo "${0} -a" >&2 echo "" >&2 echo "just check dependencies:" >&2 echo "${0} -d category/port" >&2 echo "" >&2 echo "just check if any related PRs exist:" >&2 echo "${0} -p synopsis" >&2 exit 64 } # main if ! ${GIT} diff --exit-code remotes/origin/main ; then echo "you have local commits, exiting" >&2 exit 65 fi if [ ! -r ${INDEX} ] ; then echo "${INDEX} not readable, exiting" >&2 exit 66 fi git_dir="$(${GIT} rev-parse --git-dir)" exitcode=$? if [ ${exitcode} -ne 0 ] ; then echo "not at a git boundary" >&2 exit else cd "${git_dir}/.." || exit 1 fi if [ ${#} -eq 0 ] || [ "${1}" = "-h" ] || [ "${1}" = "--help" ] ; then usage fi if [ ${1} = "-d" ] ; then if [ ${#} -ne 2 ] ; then usage fi catport=$(find_catport ${2}) check_dep ${catport} 0 ${catport} exit fi if [ ${1} = "-p" ] ; then if [ ${#} -ne 2 ] ; then usage fi get_PRs "dummy" ${2} exit fi if [ ${1} = "-F" ] ; then if [ ${#} -ne 1 ] ; then usage fi find_expired 1 exit fi if [ ${1} = "-a" ] ; then if [ ${#} -ne 1 ] ; then usage fi ${0} $(find_expired 0) exit fi gitlog=$(mktemp -t gitlog) gitrmlist=$(mktemp -t gitrmlist) echo "*/*: Remove expired ports:" > ${gitlog} echo "" >> ${gitlog} for catport in $* ; do # convert to category/port catport=$(find_catport ${catport}) cat=$(dirname ${catport}) port=$(basename ${catport}) # remove any trailing slashes catport="${cat}/${port}" pkgname=$(pkgname ${catport}) if ! check_dep ${catport} 1 "${*}" ; then continue fi if ! check_PRs ${catport} ${port} ; then continue fi - check_LEGAL ${catport} ${pkgname} - # everything seems ok, edit the files edit_MOVED ${catport} edit_Makefile ${cat} ${port} append_Template ${catport} rm_port ${catport} done if [ -s ${gitrmlist} ] ; then ${GIT} rm -r $(cat ${gitrmlist}) else log "No port directories to remove" fi # give a chance to the committer to edit files by hand and recreate/review # the diff afterwards answer=y while [ "${answer}" = "y" ] ; do ${GIT} diff --staged --irreversible-delete echo "" >&2 echo "you can now edit files by hand" >&2 answer=$(ask "do you want to recreate the diff") if [ "${answer}" = "y" ] ; then - ${GIT} add LEGAL MOVED + ${GIT} add MOVED fi done commit cleanup # EOF diff --git a/math/giacxcas/Makefile b/math/giacxcas/Makefile index ee7199c4a759..274ca4ea5df9 100644 --- a/math/giacxcas/Makefile +++ b/math/giacxcas/Makefile @@ -1,144 +1,145 @@ # Created by: Frederic Han PORTNAME= giacxcas DISTVERSION= 1.6.0-7 PORTREVISION= 1 CATEGORIES= math MASTER_SITES= http://www-fourier.ujf-grenoble.fr/~parisse/debian/dists/stable/main/source/:giac \ http://cocoa.dima.unige.it/cocoalib/tgz/:cocoa \ http://www-fourier.ujf-grenoble.fr/~parisse/giac/freebsd/ DISTFILES= giac_${DISTVERSION}${EXTRACT_SUFX}:giac \ CoCoALib-${COCOA_LIB_VERSION}.tgz:cocoa \ fltk-giac.tar.gz DIST_SUBDIR= ${PORTNAME} MAINTAINER= yuri@FreeBSD.org COMMENT= Computer algebra system LICENSE= GPLv3 DOC LICENSE_COMB= multi LICENSE_NAME_DOC= French documentation LICENSE_TEXT_DOC= French documentation is for non-commercial use only LICENSE_FILE_GPLv3= ${WRKSRC}/COPYING LICENSE_PERMS_DOC= dist-mirror pkg-mirror auto-accept +LICENSE_DISTFILES_DOC= giac_${DISTVERSION}${EXTRACT_SUFX} BUILD_DEPENDS= bash:shells/bash LIB_DEPENDS= libao.so:audio/libao \ libcurl.so:ftp/curl \ libfontconfig.so:x11-fonts/fontconfig \ libgsl.so:math/gsl \ libglpk.so:math/glpk \ libgmp.so:math/gmp \ libecm.so:math/gmp-ecm \ libmpfr.so:math/mpfr \ libmpfi.so:math/mpfi \ libpari.so:math/pari \ libpng.so:graphics/png \ libsamplerate.so:audio/libsamplerate RUN_DEPENDS= xdg-open:devel/xdg-utils USES= blaslapack compiler:c++14-lang desktop-file-utils fortran \ gettext gl gmake gnome jpeg libtool localbase ncurses perl5 \ readline shebangfix xorg GNU_CONFIGURE= yes USE_GL= gl USE_XORG= x11 xcursor xext xft xi xinerama USE_TEX= latex:build dvipsk:build USE_PERL5= run USE_LDCONFIG= yes # Warning: don't remove the USE_GCC without a runtime test *after* make install #USE_GCC= yes CONFIGURE_ENV= ac_cv_lib_X11_main=yes \ ac_cv_lib_cocoa_main=yes \ ac_cv_lib_fltk_gl_main=yes \ ac_cv_lib_fltk_main=yes \ ac_cv_lib_fltk_images_main=yes \ ac_cv_lib_jpeg_main=yes SHEBANG_FILES= src/pgiac INSTALL_TARGET= install-strip INSTALLS_ICONS= yes COCOA_LIB_VERSION= 0.99700 CPPFLAGS+= -I${COCOALIB-GIAC}/include \ -I${FLTKDEV-GIAC} \ -Wno-narrowing LDFLAGS+= -L${COCOALIB-GIAC}/lib \ -L${FLTKDEV-GIAC}/lib WRKSRC= ${WRKDIR}/giac-${DISTVERSION:C/-[0-9]*//} FLTKDEV-GIAC= ${WRKDIR}/fltk-1.3.0 COCOALIB-GIAC= ${WRKDIR}/CoCoALib-${COCOA_LIB_VERSION} TEST_TARGET= check DATADIR= ${PREFIX}/share/giac DOCSDIR= ${PREFIX}/share/doc/giac EXAMPLESDIR= ${PREFIX}/share/examples/giac INFO= giac_es giac_us OPTIONS_DEFINE= DOCS EXAMPLES #NB: built of giacxcas with USE_GCC and nlt is successfull if the ntl port is also # installed with USE_GCC CONFIGURE_ARGS+= --disable-ntl PORTDOCS= * PORTEXAMPLES= * post-extract: @${FIND} ${WRKSRC}/examples/Exemples -name "._*" -delete post-patch: @${GREP} -Rl --null /bin/bash ${COCOALIB-GIAC} | ${XARGS} -0 \ ${REINPLACE_CMD} -e 's|/bin/bash|/usr/bin/env bash|' @${REINPLACE_CMD} -e '/^SUBDIR/s|examples||' \ ${WRKSRC}/Makefile.in @${REINPLACE_CMD} -e 's|/usr/bin|${LOCALBASE}/bin|' \ ${WRKSRC}/xcas.applications @${REINPLACE_CMD} -e 's|cp -f \*|$$(INSTALL_DATA) WARNINGS [a-z]*|' \ ${WRKSRC}/doc/el/*/Makefile.in @${REINPLACE_CMD} -e '/browser=/s|"mozilla"|"xdg-open"|' \ ${WRKSRC}/src/global.cc @${FIND} ${WRKSRC} -name "Makefile.in" | ${XARGS} \ ${REINPLACE_CMD} -e 's|$$(prefix)/share/giac/doc|$$(docdir)|' @${FIND} ${COCOALIB-GIAC} -name "Makefile" | ${XARGS} \ ${REINPLACE_CMD} -e \ 's|\($$(MAKE) \)-s |\1|; s|\([[:blank:]]\)@|\1|' @${REINPLACE_CMD} -e '/.SILENT:/s|^|#|' \ ${FLTKDEV-GIAC}/makeinclude.in ${SED} -i ".orig" -Ee 's|char \*last = style \+ strlen\(style\) - 2|char *last = pretty|' \ -Ee 's|\*style = 0|last = style + strnlen(style, ENDOFBUFFER) - 2; *style = 0|' \ ${FLTKDEV-GIAC}/src/fl_set_fonts_xft.cxx pre-configure: ${REINPLACE_CMD} -e 's|/usr/lib /usr/lib64 /usr/lib32 /usr/local /opt/local/lib /sw/lib /usr/sfw/lib|${LOCALBASE}/lib|' \ -e '/libgmp.a/s|find|#find|' \ -e 's|-name libgmp.so|-type f -name "libgmp.so.*"|' \ ${COCOALIB-GIAC}/configuration/gmp-find.sh ${REINPLACE_CMD} -e 's|^protected:|public:|' ${FLTKDEV-GIAC}/FL/Fl_Widget.H .for f in icas.cc path.h ${REINPLACE_CMD} -e 's|/usr/local/share/giac/doc|${DOCSDIR}|' \ ${WRKSRC}/src/${f} .endfor ${FIND} ${WRKSRC}/doc -type f | ${XARGS} ${GREP} -l 'share/giac/doc' | \ ${XARGS} ${REINPLACE_CMD} -e 's|/usr/local/share/giac/doc|${DOCSDIR}|' @(cd ${COCOALIB-GIAC} && ${SETENV} ${CONFIGURE_ENV} ./configure \ --with-cxx="${CXX}") @(cd ${FLTKDEV-GIAC} && ${SETENV} ${CONFIGURE_ENV} ./configure \ --prefix=${FLTKDEV-GIAC} \ CXX="${CXX}" CXXFLAGS="${CXXFLAGS}") post-configure: # malloc.h on 13-CURRENT is missing mallinfo and compilation fails after it is discovered @${FIND} ${WRKSRC} -name config.h | ${XARGS} ${REINPLACE_CMD} -e 's|#define HAVE_MALLOC_H 1|/* #undef HAVE_MALLOC_H */|' pre-build: @cd ${COCOALIB-GIAC} && ${DO_MAKE_BUILD} \ CXXFLAGS_SPECIFIC="${CXXFLAGS}" library @cd ${FLTKDEV-GIAC}/src && ${DO_MAKE_BUILD} post-install-EXAMPLES-on: cd ${WRKSRC}/examples && ${COPYTREE_SHARE} "Exemples [a-z]*" \ ${STAGEDIR}${EXAMPLESDIR} .include diff --git a/ports-mgmt/portlint/Makefile b/ports-mgmt/portlint/Makefile index c7a08584c901..a4d37b63d107 100644 --- a/ports-mgmt/portlint/Makefile +++ b/ports-mgmt/portlint/Makefile @@ -1,47 +1,48 @@ # Created by: Jun-ichiro itojun Hagino PORTNAME= portlint PORTVERSION= 2.19.6 +PORTREVISION= 1 CATEGORIES= ports-mgmt MASTER_SITES= # none DISTFILES= # none MAINTAINER= marcus@FreeBSD.org COMMENT= Verifier for FreeBSD port directory LICENSE= ITOJUN LICENSE_NAME= Itojun License LICENSE_TEXT= All rights reserved. Freely redistributable. Absolutely no warranty. LICENSE_PERMS= auto-accept dist-mirror dist-sell pkg-mirror pkg-sell NO_ARCH= yes NO_BUILD= yes WRKSRC= ${WRKDIR}/src USES= perl5 shebangfix SHEBANG_FILES= *.pl USE_PERL5= run SRC= ${.CURDIR}/src PLIST_FILES= bin/portlint \ man/man1/portlint.1.gz PORTEXAMPLES= portlintgrep OPTIONS_DEFINE= EXAMPLES do-extract: @${CP} -R ${SRC}/ ${WRKSRC} do-install: ${INSTALL_SCRIPT} ${WRKSRC}/portlint.pl \ ${STAGEDIR}${PREFIX}/bin/portlint ${INSTALL_MAN} ${WRKSRC}/portlint.1 \ ${STAGEDIR}${MAN1PREFIX}/man/man1 do-install-EXAMPLES-on: ${MKDIR} ${STAGEDIR}${EXAMPLESDIR} ${INSTALL_SCRIPT} ${WRKSRC}/portlintgrep.pl \ ${STAGEDIR}${EXAMPLESDIR}/portlintgrep .include diff --git a/ports-mgmt/portlint/src/portlint.pl b/ports-mgmt/portlint/src/portlint.pl index d797c4c6a11f..57c61cea5c01 100644 --- a/ports-mgmt/portlint/src/portlint.pl +++ b/ports-mgmt/portlint/src/portlint.pl @@ -1,3953 +1,3944 @@ #! /usr/bin/perl -w # ex:ts=4 # # portlint - lint for port directory # implemented by: # Jun-ichiro itojun Hagino # Yoshishige Arai # # Copyright(c) 1997 by Jun-ichiro Hagino . # All rights reserved. # Freely redistributable. Absolutely no warranty. # # Please note that this perl code used to be able to handle (Open|Net|Free)BSD # bsd.port.mk. There are significant differences in those so non-FreeBSD code # was removed. # # $MCom: portlint/portlint.pl,v 1.528 2021/05/14 16:53:31 jclarke Exp $ # use strict; use warnings; use Getopt::Std; use File::Find; use IPC::Open2; use File::Basename; use POSIX qw(strftime); sub perror($$$$); our ($opt_a, $opt_A, $opt_b, $opt_C, $opt_c, $opt_g, $opt_h, $opt_m, $opt_t, $opt_v, $opt_M, $opt_N, $opt_B, $opt_V, @ALLOWED_FULL_PATHS); my ($err, $warn); my ($extrafile, $parenwarn, $committer, $verbose, $usetabs, $newport, $grouperrs, $checkmfiles); my $contblank; my $portdir; my $makeenv = ""; my @errlst = (); my %errcache = (); $err = $warn = 0; $extrafile = $parenwarn = $committer = $verbose = $usetabs = $newport = 0; $checkmfiles = 0; $contblank = 1; $portdir = '.'; @ALLOWED_FULL_PATHS = qw(/boot/loader.conf /compat/ /dev/null /etc/inetd.conf); # version variables my $major = 2; my $minor = 19; my $micro = 6; # default setting - for FreeBSD my $portsdir = '/usr/ports'; my $rcsidstr = 'FreeBSD'; my $localbase = '/usr/local'; my $numpitems = 6; my %lang_pref = qw( arabic ar chinese zh french fr german de hebrew iw hungarian hu japanese ja korean ko polish pl portuguese pt russian ru ukrainian uk vietnamese vi ); my @lang_cat = keys %lang_pref; my @lang_short = values %lang_pref; my $re_lang_short = '(' . join('|', @lang_short) . ')-'; my ($prog) = ($0 =~ /([^\/]+)$/); sub usage { print STDERR <); close(MK); my $cmd = join(' -V MASTER_SITE_', "make $makeenv ", @site_groups); $i = 0; open2(\*IN, \*OUT, $cmd); close(OUT); while () { my $g = $site_groups[$i]; for my $s (split()) { $predefined{$s} = $g; } $i++; } close(IN); open(MK, 'Makefile') || die "Makefile: $!"; my $ulineno = -1; my $uulineno = -1; my @muses = (); my @omuses = (); while (my $mline = ) { if ($uulineno == -1 && $mline =~ /^USE_/) { $uulineno = $.; } if ($mline =~ /^USES[?+]?=\s*(.*)/) { if ($ulineno == -1) { $ulineno = $.; } if ($1) { push @muses, split(/\s+/, $1); } } if ($mline =~ /^[\w\d]+_USES[?+]?=\s*(.*)/) { if ($1) { push @omuses, split(/\s+/, $1); } } } if ($uulineno > -1 && $ulineno > -1 && $uulineno < $ulineno) { &perror("WARN", 'Makefile', $uulineno, "USE_* seen before USES. ". "According to the porters-handbook, USES must appear first."); } my %hmuses = map { $_ => 1 } @muses; foreach my $omuse (@omuses) { if ($hmuses{$omuse}) { &perror("WARN", 'Makefile', -1, "$omuse is specified in both USES ". "and a optional *_USES. It only needs to be specified in one."); } } foreach my $muse (@muses) { $makevar{USES} .= " " . $muse; } # # check for files. # my @checker = ($makevar{DESCR}, 'Makefile', $makevar{DISTINFO_FILE}); my %checker = ( $makevar{DESCR} => \&checkdescr, 'Makefile' => \&checkmakefile, $makevar{DISTINFO_FILE} => \&checkdistinfo ); if ($extrafile) { my @files = ( <$makevar{SCRIPTDIR}/*>, @makevar{qw(DESCR PLIST PKGINSTALL PKGDEINSTALL PKGREQ PKGMESSAGE)} ); foreach my $i (@files) { next if (! -T $i); next if (defined $checker{$i}); if ($i =~ /\bpkg-plist/) { unshift(@checker, $i); $checker{$i} = \&checkplist; } else { push(@checker, $i); $checker{$i} = \&checkpathname; } } } if ($checkmfiles) { foreach my $i ($mfile_moved, $mfile_uids, $mfile_gids) { next if (! -T $i); next if (defined $checker{$i}); push(@checker, $i); $checker{$i} = \&checkmfile; } } foreach my $i (<$makevar{FILESDIR}/patch-*>) { next if (! -T $i); next if (defined $checker{$i}); push(@checker, $i); $checker{$i} = \&checkpatch; } foreach my $i (@checker) { print "OK: checking $i.\n" if ($verbose); if (! -f "$i") { &perror("FATAL", "", -1, "no $i in \"$portdir\".") unless $i eq $makevar{DISTINFO_FILE} && $makevar{DISTFILES} eq ""; } else { my $proc = $checker{$i}; &$proc($i) || &perror("", "", -1, "Cannot open the file $i\n"); if ($proc ne \&checkpatch) { &checklastline($i) || &perror("", "", -1, "Cannot open the file $i\n"); } } } checkpatches(<$makevar{FILESDIR}/patch-*>); if ($committer) { sub find_proc { return if /^\.\.?$/; (my $fullname = $File::Find::name) =~ s#^\./##; print "OK: checking the file name of $fullname.\n" if ($verbose); if ($fullname eq 'work') { &perror("FATAL", $fullname, -1, "be sure to cleanup the working directory ". "before committing the port."); $File::Find::prune = 1; } elsif (-l) { &perror("WARN", $fullname, -1, "this is a symlink. ". "Please remove it."); } elsif (-z) { &perror("FATAL", $fullname, -1, "empty file and should be removed. ". "If it still needs to be there, put a dummy comment ". "to state that the file is intentionally left empty."); } elsif (-d && scalar(my @x = <$_/{*,.?*}>) <= 1) { &perror("FATAL", $fullname, -1, "empty directory should be removed.") unless ($fullname =~ /^\.svn/ || $fullname =~ /^\.git/); } elsif (/^\./) { &perror("WARN", $fullname, -1, "dotfiles are not preferred. ". "If this file is a dotfile to be installed as an example, ". "consider importing it as \"dot$_\".") unless (-d && ($_ eq '.svn' || $_ eq '.git')); } elsif (/[^-.a-zA-Z0-9_\+]/) { &perror("WARN", $fullname, -1, "only use characters ". "[-_.a-zA-Z0-9+] for patch or script names."); } elsif (/\.(orig|rej|bak)$/ || /~$/ || /^\#/) { &perror("FATAL", $fullname, -1, "for safety, be sure to cleanup ". "backup files before committing the port."); } elsif (/(^|\.)core$/) { &perror("FATAL", $fullname, -1, "for safety, be sure to cleanup ". "core files before committing the port."); } elsif (/README.html/) { &perror("FATAL", $fullname, -1, "for safety, be sure to cleanup ". "README.html files before committing the port."); } elsif (($_ eq '.svn' || $_ eq '.git') && -d) { &perror("FATAL", $fullname, -1, "for safety, be sure to cleanup ". "Subversion files before committing the port."); $File::Find::prune = 1; } elsif ($_ eq 'CVS' && -d) { if ($newport) { &perror("FATAL", $fullname, -1, "for safety, be sure to cleanup ". "CVS directories before importing the new port."); } $File::Find::prune = 1; } elsif (-f) { my $fullpath = $makevar{'.CURDIR'}.'/'.$fullname; my $result = `type svn >/dev/null 2>&1 && svn -q status $fullpath`; chomp $result; if (substr($result, 0, 1) eq '?') { &perror("FATAL", "", -1, "$fullname not under SVN.") unless (eval { /$ENV{'PL_SVN_IGNORE'}/, 1 } && /$ENV{'PL_SVN_IGNORE'}/); } } } find(\&find_proc, '.'); # Check for ports that may break INDEX my $indexerr = `env LOCALBASE=/nonexistentlocal make $makeenv describe 2>&1 >/dev/null`; chomp $indexerr; $indexerr =~ tr/\n/ /s; &perror("FATAL", "", -1, "breaks INDEX ($indexerr).") if ($indexerr); # Suggest to set DEVELOPER knob in /etc/make.conf if (!$makevar{DEVELOPER}) { &perror("WARN", "", -1, "Consider to set DEVELOPER=yes in /etc/make.conf"); } } if ($err || $warn) { my($errtext, $warntext) = ("error", "warning"); $errtext .= "s" unless ($err == 1); $warntext .= "s" unless ($warn == 1); if ($grouperrs) { foreach my $msg (@errlst) { my $lines; if ($errcache{$msg} && scalar(@{$errcache{$msg}})) { $lines = "[" . (join(",", @{$errcache{$msg}})) . "]: "; } else { $lines = ""; } $msg =~ s/%%LINES%%/$lines/; print $msg, "\n"; } } printf("%d fatal %s and %d %s found.\n", $err, $errtext, $warn, $warntext); } else { print "looks fine.\n"; } exit $err; # # distinfo # sub checkdistinfo { my($file) = @_; my($dist_subdir) = $makevar{DIST_SUBDIR}; my(@allfiles) = split (/\s+/, $makevar{ALLFILES}); my %algorithms = (); my %records = (); foreach my $i (split (/\s+/, uc ($makevar{CHECKSUM_ALGORITHMS}))) { $algorithms{$i} = 1; } open(IN, "< $file") || return 0; while () { if (/^\s*$/) { &perror("FATAL", $file, $., "found blank line."); next; } if (/^TIMESTAMP\s+=\s+(\d+)$/) { my $now = time; if ($1 > $now) { &perror("FATAL", $file, $., "TIMESTAMP is in the future"); } next; } if (/(\S+)\s+\((\S+)\)\s+=\s+(\S+)/) { my ($tag, $path, $value) = ($1, $2, $3); $records{$path}{$tag} = $value; if (!$algorithms{$tag} && $tag ne "SIZE") { &perror("FATAL", $file, $., "unsupported checksum algorithm ". "found: $tag."); } } else { &perror("FATAL", $file, $., "line format error."); } } close(IN); # For all files check SIZE and checksums foreach my $f (@allfiles) { # Remove hindering chars from DISTNAME $f =~ s/['\\]//g; my $path = $f; $path = "$dist_subdir/$f" if $dist_subdir; if (!defined($records{$path}{SIZE})) { &perror("FATAL", $file, -1, "has no SIZE record for $path."); } my $n = 0; foreach my $alg (keys %algorithms) { $n++ if exists($records{$path}{$alg}); } if ($n == 0) { &perror("FATAL", $file, -1, "no checksum record for $path."); } if ($n < scalar(keys %algorithms)) { &perror("WARN", $file, -1, "no checksum records for all ". "supported algorithms (".join(" ",keys %algorithms).") for ". "$path."); } } return 1; } # # pkg-descr # sub checkdescr { my($file) = @_; my(%maxchars) = ($makevar{DESCR}, 80); my(%maxlines) = ($makevar{DESCR}, 24); my(%minlines) = ($makevar{DESCR}, 3); my(%toolongerrmsg) = ($makevar{DESCR}, "exceeds $maxlines{$makevar{DESCR}} ". "lines, make it shorter if possible."); my(%tooshorterrmsg) = ($makevar{DESCR}, "contains less than $minlines{$makevar{DESCR}} ". "lines, make it longer if possible."); my($longlines, $linecnt, $tmp) = (0, 0, ""); open(IN, "< $file") || return 0; while () { if ($_ =~ /[ \t]+\n?$/) { &perror("WARN", $file, $., "whitespace before end ". "of line."); } $tmp .= $_; chomp || &perror("WARN", $file, -1, "lines should terminate with a ". "newline (i.e. '\\n')."); if (/ $/) { &perror("WARN", $file, -1, "lines should not contain carriage ". "returns. Strip all carriage returns (e.g. run dos2unix) ". "in $file."); } if (/^WWW:(\s+)(\S*)/) { my $wwwurl = $2; if ($1 ne ' ') { &perror("WARN", $file, -1, "use WWW: with a single space, ". "then $wwwurl"); } if ($wwwurl !~ m|^https?://|) { &perror("WARN", $file, -1, "WWW URL, $wwwurl should begin ". "with \"http://\" or \"https://\"."); } if ($wwwurl =~ m|^http://search.cpan.org/~|) { &perror("WARN", $file, -1, "consider changing WWW URL to ". "http://search.cpan.org/dist/$makevar{PORTNAME}/"); } } $linecnt++; $longlines++ if ($maxchars{$file} < length); } if ($linecnt > $maxlines{$file}) { &perror("WARN", $file, -1, "$toolongerrmsg{$file}". "(currently $linecnt lines)"); } elsif ($linecnt < $minlines{$file}) { &perror("WARN", $file, -1, "$tooshorterrmsg{$file}". "(currently $linecnt ".($linecnt > 1 ? "lines" : "line").")"); } else { print "OK: $file: has $linecnt lines.\n" if ($verbose); } if ($longlines > 0) { &perror("WARN", $file, -1, "includes lines that exceed $maxchars{$file} ". "characters."); } if ($tmp =~ /[\033\200-\377]/) { &perror("WARN", $file, -1, "includes iso-8859-1, or ". "other local characters. files should be in ". "plain 7-bit ASCII"); } if ($file =~ /\bpkg-descr/ && $tmp =~ m,https?://,) { my $has_url = 0; my $has_www = 0; my $cpan_url = 0; my $has_endslash = 0; foreach my $line (grep($_ =~ "https?://", split(/\n+/, $tmp))) { $has_url = 1; if ($line =~ m,WWW:[ \t]+https?://,) { $has_www = 1; if ($line =~ m,search.cpan.org,) { $cpan_url = 1; if ($line =~ m,/$,) { $has_endslash = 1; } } } } if (!$has_url) { &perror("WARN", $file, -1, "add \"WWW: URL:\" for this port if possible"); } if ($cpan_url && !$has_endslash) { &perror("WARN", $file, -1, "end WWW CPAN URL with a \"/\""); } if ($has_url && ! $has_www) { &perror("FATAL", $file, -1, "contains a URL but no \"WWW:\""); } } close(IN); } # # pkg-plist # sub checkplist { my($file) = @_; my($curdir) = ($localbase); my $seen_special = 0; my $item_count = 0; my $owner_seen = 0; my $group_seen = 0; my $found_so = 0; my $found_naked_so = 0; # Variables that are allowed to be out-of-sync in the XXXDIR check. # E.g., %%PORTDOCS%%%%RUBY_MODDOCDIR%% will be OK because there is # no %%PORTRUBY_MODDOC%% substitution. my %check_xxxdir_ok = ( "DOCS" => "DOCS", "EXAMPLES" => "EXAMPLES", "DATA" => "DATA", "RUBY_DOC" => "DOCS", "RUBY_EXAMPLES" => "EXAMPLES", "RUBY_MODDOC" => "DOCS", "RUBY_MODEXAMPLES" => "EXAMPLES", ); open(IN, "< $file") || return 0; while () { $item_count++; if ($_ =~ /[ \t]+\n?$/) { &perror("WARN", $file, $., "whitespace before end ". "of line."); } # make it easier to handle. $_ =~ s/\s+$//; $_ =~ s/\n$//; if ($_ eq "") { &perror("WARN", $file, $., "empty line found in plist."); } # store possible OPTIONS knobs for OPTIONS_SUB if ($makevar{OPTIONS_SUB}) { while (/\%\%([^%]+)\%\%/g) { if ($1 =~ /PORTDOCS/) { push @popt, "DOCS"; } elsif ($1 =~ /PORTEXAMPLES/) { push @popt, "EXAMPLES"; } else { push @popt, $1; } } } if ($_ =~ /\.DS_Store/) { &perror("WARN", $file, $., ".DS_Store meta data files must not ". "be installed."); } if (m'lib/perl5/site_perl/mach/%%PERL_VER%%') { &perror("WARN", $file, $., "use \%\%SITE_ARCH\%\% ". "instead of lib/perl5/site_perl/mach/\%\%PERL_VER\%\%"); } elsif (m'lib/perl5/site_perl') { &perror("WARN", $file, $., "use \%\%SITE_PERL\%\% ". "instead of lib/perl5/site_perl."); } if (m'([\w\d]+-portbld-freebsd\d+\.\d+)') { &perror("WARN", $file, $., "possible direct use of the ". "CONFIGURE_TARGET value ($1). Consider using the plist ". "substitution %%CONFIGURE_TARGET%% instead."); } if (m'\@dirrm(try)?\s+libdata/pkgconfig') { &perror("FATAL", $file, $., "libdata/pkgconfig should not be ". "removed. It is listed in BSD.local.dist."); } if (m'\@dirrm(try)?\s') { &perror("WARN", $file, $., "\@dirrm[try] is deprecated. If you ". "require special directory handling, use \@dir instead and ". "consult the porter's handbook."); } if (m'\@cwd') { &perror("WARN", $file, $., "\@cwd is deprecated. Please use ". "absolute pathnames instead."); } if (m'\@stopdaemon\s') { &perror("WARN", $file, $., "\@stopdaemon is deprecated. The ". "pkg(8) has a generic mechanism to provide the same function, ". "see HANDLE_RC_SCRIPTS in pkg.conf(5)."); } if (m'^\@(un)?exec') { &perror("WARN", $file, $., "@[un]exec is deprecated in ". "favor of \@[un]exec as the latter specifies ". "the exact part of the pkg lifecycle the commands need ". "to run"); } $seen_special++ if /[\%\@]/; if ($_ =~ /^\@/) { if ($_ =~ /^\@(cwd|cd)[ \t]+(\S+)/) { $curdir = $2; } elsif ($_ =~ /^\@unexec[ \t]+rm[ \t]/) { if ($_ !~ /%[DB]/) { &perror("WARN", $file, $., "use \"%D\" or \"%B\" to ". "specify prefix."); } if ($_ !~ /true$/ && $_ !~ /rm -f/) { &perror("WARN", $file, $., "add \"2>&1 ". ">/dev/null || true\" ". "to \"\@unexec rm\"."); } } elsif ($_ =~ /^\@unexec[ \t]+rmdir/) { if ($_ !~ /%[DB]/) { &perror("WARN", $file, $., "use \"%D\" or \"%B\" to ". "specify prefix."); } &perror("WARN", $file, $., "use \"\@dirrmtry\" ". "instead of \"\@unexec rmdir\"."); } elsif ($_ =~ /^\@unexec[ \t]+install-info[ \t]+--delete\s+(.+)\s+(.+)$/) { &perror("WARN", $file, $., "\@unexec install-info is deprecated in favor of adding info files into the Makefile using the INFO macro."); } elsif ($_ =~ /^\@(pre|post)?(exec|unexec|)/) { if (/ldconfig/) { &perror("WARN", $file, $., "possible ". "direct use of ldconfig ". "in PLIST found. use ". "USE_LDCONFIG instead."); } if (/scrollkeeper/) { &perror("WARN", $file, $., "possible ". "direct use of scrollkeeper commands ". "in PLIST found. Use ". "INSTALLS_OMF instead ". "(see http://www.FreeBSD.org/gnome/docs/porting.html ". "for more details)."); } } elsif ($_ =~ /^\@(comment)/) { &perror("FATAL", $file, $., "\$$rcsidstr\$ is deprecated in Git.") if (/\$$rcsidstr[:\$]/); } elsif ($_ =~ m!^\@(dirrm|dirrmtry)\s+/!) { &perror("WARN", $file, $., "Using \@$1 with absolute path ". "will not work as you expected in most cases. Use ". "\@dir... if you want to remove a directory such as ". "/var/\${PORTNAME}"); } elsif ($_ eq "\@cwd") { ; # @cwd by itself means change directory back to the original # PREFIX. } elsif ($_ =~ /^\@\(/) { if ($_ !~ /^\@\([^,]*,[^,]*,[^\),]*(,[^\)]*)?\)/) { &perror("WARN", $file, $., "Invalid use of \@(...). ". "Arguments should be owner,group,perms[,fflags]"); } } elsif ($_ =~ /^\@sample\s+(.+)/) { my $sl = $.; my @sampleparts = split(/\s+/, $1); if (scalar @sampleparts == 1 && $sampleparts[0] !~ /\.sample$/) { &perror("WARN", $file, $sl, "\@sample directive references". " file that does not end in ``.sample''. Sample". " files must end in ``.sample''."); } } elsif ($_ =~ /^\@owner/) { if ($_ =~ /^\@owner\s+.+/) { if ($owner_seen > 0) { &perror("WARN", $file, $., "Nested setting of \@owner ". "found. Reset \@owner before setting it again."); } $owner_seen++; } else { if ($owner_seen == 0) { &perror("WARN", $file, $., "\@owner reset seen before ". "a new owner section was started."); } $owner_seen--; } } elsif ($_ =~ /^\@group/) { if ($_ =~ /^\@group\s+.+/) { if ($group_seen > 0) { &perror("WARN", $file, $., "Nested setting of \@group ". "found. Reset \@group before setting it again."); } $group_seen++; } else { if ($group_seen == 0) { &perror("WARN", $file, $., "\@group reset seen before ". "a new group section was started."); } $group_seen--; } } elsif ($_ =~ /^\@(dir|dirrm|dirrmtry|rmtry|option|stopdaemon|owner|group|mode|fc|fcfontsdir|fontsdir|info|shell)\b/) { ; # no check made } else { &perror("WARN", $file, $., "unknown pkg-plist directive \"$_\""); } next; } if ($_ =~ /charset\.alias$/ || $_ =~ /locale\.alias$/) { &perror("WARN", $file, $., "installing charset.alias or locale.alias, ". "please add USES[+]=gettext and use libintl from devel/gettext ". "instead of from outdated bundled one if possible. ". "See http://www.freebsd.org/cgi/query-pr.cgi?pr=ports/71531 ". "for more details."); } if ($_ =~ /\%gconf.*\.xml/ || $_ =~ /gconf.*\.schemas?/) { &perror("FATAL", $file, $., "explicitly listing \%gconf key files ". "or GConf schema files in the plist is not supported. ". "Use GCONF_SCHEMAS in the Makefile instead. ". "See http://www.FreeBSD.org/gnome/docs/porting.html ". "for more details."); } if ($_ =~ m|lib/pkgconfig/[^\/]+.pc$|) { &perror("FATAL", $file, $., "installing pkg-config files into ". "lib/pkgconfig. All pkg-config files must be installed ". "into libdata/pkgconfig for them to be found by pkg-config."); } if ($_ =~ m|lib[^\/]+\.so\.\d+$| && $makevar{USE_LDCONFIG} eq '') { &perror("WARN", $file, $., "installing shared libraries, ". "please define USE_LDCONFIG as appropriate"); } elsif ($_ =~ m|lib[^\/]+\.so\.\d+$|) { $found_so++; } elsif ($_ =~ m|lib[^\/]+\.so$|) { $found_naked_so++; } if ($_ =~ m|^share/icons/.*/| && $makevar{INSTALLS_ICONS} eq '' && needs_installs_icons()) { &perror("WARN", $file, $., "installing icons, ". "please define INSTALLS_ICONS as appropriate"); } if ($_ =~ m|\.omf$| && $makevar{INSTALLS_OMF} eq '') { &perror("WARN", $file, $., "installing OMF files, ". "please define INSTALLS_OMF (see the FreeBSD GNOME ". "porting guide at ". "http://www.FreeBSD.org/gnome/docs/porting.html ". "for more details)"); } if ($_ =~ m|^(%%([^%]+)%%)?.*\.mo$| && $makevar{USES} !~ /\bgettext\b/) { my $show_nls_warn = 1; if ($2) { my $mv = get_makevar($2."_USES"); if ($mv =~ /\bgettext\b/) { $show_nls_warn = 0; } } if ($show_nls_warn) { &perror("WARN", $file, $., "installing gettext translation files, ". "please define USES[+]=gettext as appropriate"); } } if ($_ =~ m|\.core$| && $_ !~ /^\@/) { &perror("WARN", $file, $., "this port installs a file which ends ". "in \".core\". This file may be deleted if ". "daily_clean_disks_enable=\"YES\" in /etc/periodic.conf. ". "If possible, install this file with a different name."); } if ($_ =~ m|/a\.out$| && $_ !~ /^\@/) { &perror("WARN", $file, $., "this port installs a file named ". "\"a.out\". This file may be deleted if ". "daily_clean_disks_enable=\"YES\" in /etc/periodic.conf. ". "If possible, install this file with a different name."); } if ($_ =~ /\.info$/) { &perror("WARN", $file, $., "enumerating info files in the plist is deprecated in favor of adding info files into the Makefile using the INFO macro."); } if ($_ =~ /\.info-\d+$/) { &perror("FATAL", $file, $., "numbered info files are obsolete and not portable; add info files using the INFO macro in the Makefile."); } if ($_ =~ /^(\%\%PORTDOCS\%\%)?share\/doc\//) { &perror("WARN", $file, $., "If and only if your port is ". "DOCSDIR-safe (that is, a user can override DOCSDIR ". "when building this port and the port will still work ". "correctly) consider using DOCSDIR macro; if you are ". "unsure if this port is DOCSDIR-safe, then ignore ". "this warning"); $sharedocused++; } elsif ($_ =~ /^(\%\%PORTDOCS\%\%)?\%\%DOCSDIR\%\%/) { $sharedocused++; } if ($_ =~ /^share\/examples\//) { &perror("WARN", $file, $., "If and only if your port is ". "EXAMPLESDIR-safe (that is, a user can override EXAMPLESDIR ". "when building this port and the port will still work ". "correctly) consider using EXAMPLESDIR macro; if you are ". "unsure if this port is EXAMPLESDIR-safe, then ignore this ". "warning"); } { my $tmpportname = quotemeta($makevar{PORTNAME}); if ($_ =~ /^share\/$tmpportname\//) { &perror("WARN", $file, $., "If and only if your port is ". "DATADIR-safe (that is, a user can override DATADIR when ". "building this port and the port will still work ". "correctly) consider using DATADIR macro; if you are ". "unsure if this port is DATADIR-safe, then ignore this ". "warning"); } } if ($_ =~ m{^%%PORT(\w+)%%(.*?)%%(\w+)DIR%%(.*)$} and $1 ne $3 and defined($check_xxxdir_ok{$3})) { &perror("WARN", $file, $., "Do not mix %%PORT$1%% with %%$3DIR%%. ". "Use '%%PORT$check_xxxdir_ok{$3}%%$2%%$3DIR%%$4' instead and update Makefile ". "accordingly.") unless ($check_xxxdir_ok{$3} eq $1); } # It is now recommended for manpages to be installed under share/man. #if ($_ =~ m#share/man/#) { # &perror("FATAL", $file, $., "Man pages must be installed into ". # "``man'' not ``share/man''."); #} if ($_ =~ m#man/([^/]+/)?man[1-9ln]/([^\.]+\.[1-9ln])(\.gz)?$#) { if (!$3) { &perror("FATAL", $file, $., "Unpacked man file $2 listed. ". "Must be gzipped."); } } if ($curdir !~ m#^$localbase#) { &perror("WARN", $file, $., "installing to ". "directory $curdir discouraged. ". "could you please avoid it?"); } if ("$curdir/$_" =~ m#^$localbase/share/doc/#) { print "OK: $file [$.]: seen installation to share/doc. ". "($curdir/$_)\n" if ($verbose); $sharedocused++; } } if ($owner_seen > 0) { &perror("WARN", $file, -1, "A \@owner section was started but never ". "reset. USe \@owner without any arguments to reset the owner"); } if ($group_seen > 0) { &perror("WARN", $file, -1, "A \@group section was started but never ". "reset. Use \@group without any arguments to reset the group"); } if (!$seen_special && $item_count < $numpitems) { &perror("WARN", $file, -1, "There are only $item_count items in the plist. Consider using PLIST_FILES instead of pkg-plist when installing less than $numpitems items."); } if ($makevar{USE_LDCONFIG} ne '' && !$found_so) { if ($found_naked_so) { &perror("WARN", $file, -1, "You have defined USE_LDCONFIG, but this ". "port does not install shared objects in the format lib*.so.[0-9] ". "which ldconfig(8) needs to register them in the hints file."); } else { &perror("WARN", $file, -1, "You have defined USE_LDCONFIG, but this ". "port does not install any shared objects."); } } close(IN); 1; } # # ${PORTSDIR}/MOVED, UIDs, GIDs files # sub checkmfile { my ($file) = @_; my $line = 0; my $format; my @entries; my @sorted; my $dosort; if ($file =~ m/MOVED$/) { $format = '^[^|]*\|[^|]*\|[^|]*\|[^|]*$'; $dosort = 0; } elsif ($file =~ m/UIDs$/) { $format = '^[^:]+:\*:[0-9]+:[0-9]+:[^:]*:0:0:[^:]+:[^:]+:[^:]+$'; $dosort = 1; } elsif ($file =~ m/GIDs$/) { $format = '^[^:]+:\*:[0-9]+:[^:]*$'; $dosort = 1; } else { &perror("FATAL", $file, -1, "Internal error. ". "Invalid name for mfiles."); } open(IN, "<$file") || return 0; while () { chomp; $line++; next if (m,^\s*#,); if (!m,${format},) { &perror("FATAL", $file, -1, "malformed line at ". "${line}.\n => $_"); } else { push @entries, "$line:$_"; next; } } if ($dosort) { my $errline; @sorted = sort {(split /:/, $a)[3] <=> (split /:/, $b)[3] } @entries; for (my $n = 0; $n < @entries; $n++) { if (!defined($sorted[$n]) or $entries[$n] ne $sorted[$n]) { ($line, $errline) = ($entries[$n] =~ m/([0-9]+):(.*)/); &perror("WARN", $file, -1, "malformed sorting order at " . "${line}.\n => $errline"); } } } close(IN); } # # misc files # sub checkpathname { my($file) = @_; my($whole); open(IN, "< $file") || return 0; $whole = ''; while () { $whole .= $_; } &abspathname($whole, $file); close(IN); } sub checklastline { my($file) = @_; my($whole); open(IN, "< $file") || return 0; $whole = ''; while () { $whole .= $_; } if ($whole !~ /\n$/) { &perror("FATAL", $file, -1, "the last line has to be ". "terminated by \\n."); } if ($whole =~ /\n([ \t]*\n)+$/) { &perror("WARN", $file, -1, "seems to have unnecessary blank lines ". "at the last part."); } close(IN); } sub checkpatches { my (@patchfiles) = @_; my @patched_files; foreach my $file (@patchfiles) { open(IN, "< $file") || return 0; while () { if ($_ =~ /^\+\+\+\s(.*?)\s.*/) { #if($1 ~~ @patched_files) { if (grep {$_ eq $1} @patched_files) { &perror("WARN", $file, -1, "$1 patched multiple times"); } else { push(@patched_files, $1); } } } } } sub checkpatch { my($file) = @_; my($whole); if (-z "$file") { &perror("FATAL", $file, -1, "has no content. should be removed ". "from repository."); return; } my $bfile = basename($file); if (length $bfile > 100) { &perror("FATAL", $file, -1, "make sure patch file names contain no ". "more than 100 characters."); } open(IN, "< $file") || return 0; $whole = ''; my $checked_header = 0; while () { $whole .= $_; if (/^--- / && !$checked_header) { $checked_header = 1; if ($_ !~ /UTC\s*$/) { &perror("WARN", $file, -1, "patch was not generated using ". "``make makepatch''. It is recommended to use ". "``make makepatch'' when you need to [re-]generate a ". "patch to ensure proper patch format."); } } } if ($committer && $whole =~ /\wjavavm\w/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "since javavmwrapper 2.0, the ". "``javavm'' command to invoke a JVM is deprecated. Use ". "``java'' instead"); } if ($whole =~ / /) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "patch contains ^M characters. ". "Consider defining USES=dos2unix to remove DOS line endings ". "from source files."); } if ($whole !~ /\n$/s) { &perror("FATAL", $file, -1, "patch does not end with a newline, and the commit check ". "hook will fail."); } close(IN); } sub check_depends_syntax { my $tmp = shift; my $file = shift; my (%seen_depends, $j); $ENV{'PORTSDIR'} //= $portsdir; foreach my $i (grep(/^(PATCH_|EXTRACT_|LIB_|BUILD_|RUN_|TEST_|FETCH_)*DEPENDS[?+]?=/, split(/\n/, $tmp))) { $i =~ s/^((PATCH_|EXTRACT_|LIB_|BUILD_|RUN_|TEST_|FETCH_)*DEPENDS)[?+]?=[ \t]*//; $j = $1; $seen_depends{$j}++; if ($j ne 'DEPENDS' && $i =~ /^\$\{([A-Z_]+DEPENDS)}\s*$/ && $seen_depends{$1} && $j ne $1) { print "OK: $j refers to $1, skipping checks.\n" if ($verbose); next; } elsif ($j ne 'DEPENDS' && $i =~ /^\$\{([A-Z_]+DEPENDS)}\s*$/ && !$seen_depends{$1}) { # make(1) does lazy variable evaluation, so we can use a variable before it is # declared. However, portlint scans line by line. Allow this behavior. print "OK: $j refers to $1 (which hasn't been declared yet, but it will work), skipping checks.\n" if ($verbose); next; } print "OK: checking ports listed in $j.\n" if ($verbose); my @ks = split(/\s+/, $i); while (@ks) { my $k = shift @ks; if ($k =~ /^#/) { last; } my $ok = $k; if ($k =~ /^\$\{([^\}]+)\}$/) { $k = get_makevar_shallow($1); push @ks, split(/\s+/, $k); next; } if ($k eq '') { next; } my @l = split(':', $k); print "OK: checking dependency value for $j.\n" if ($verbose); if ($ok =~ /\$\{((PATCH_|EXTRACT_|LIB_|BUILD_|RUN_|TEST_|FETCH_)*DEPENDS)}/) { &perror("WARN", $file, -1, "do not set $j to $ok. ". "Instead, explicity list out required $j dependencies."); } if (($j ne 'DEPENDS' && scalar(@l) != 2 && scalar(@l) != 3)) { &perror("WARN", $file, -1, "wrong dependency value ". "for $j. $j requires ". "2 or 3 ". "colon-separated tuples."); next; } my %m = (); $m{'dep'} = $l[0]; my ($di, $fl) = split(/\@/, $l[1]); $m{'dir'} = $di; $m{'fla'} = $fl // ''; $m{'tgt'} = $l[2] // ''; my %depmvars = (); foreach my $dv ($m{'dep'}, $m{'dir'}, $m{'tgt'}) { foreach my $mv ($dv =~ /\$\{([^}]+)\}/g) { if (defined($depmvars{$mv})) { next; } if (defined($makevar{$mv})) { $depmvars{$mv} = $makevar{$mv}; } else { $depmvars{$mv} = &get_makevar($mv); } } } # check Python flavor my $bdir = basename($m{'dir'}); if ($bdir =~ /^py-/) { if ($m{'fla'} ne '${PY_FLAVOR}') { &perror("WARN", $file, -1, "you may want directory for ". "dependency $m{'dep'} to be $m{'dir'}\@\${PY_FLAVOR}"); } } # check JAVALIBDIR if ($m{'dep'} =~ m|share/java/classes|) { &perror("FATAL", $file, -1, "you should use \${JAVALIBDIR} ". "in BUILD_DEPENDS/RUN_DEPENDS to define ". "dependencies on JAR files installed in ". "\${JAVAJARDIR}"); } foreach my $dv ($m{'dep'}, $m{'dir'}, $m{'tgt'}) { foreach my $dmv (keys %depmvars) { $dv =~ s/\$\{$dmv\}/$depmvars{$dmv}/g; } } print "OK: dep=\"$m{'dep'}\", ". "dir=\"$m{'dir'}\", tgt=\"$m{'tgt'}\"\n" if ($verbose); # check USES=perl5 if ($m{'dep'} =~ /^perl5(\.\d+)?$/) { &perror("WARN", $file, -1, "dependency to perl5 ". "listed in $j. consider using ". "USES[+]=perl5."); } # Check for ${SITE_PERL} in depends if ($m{'dep'} =~ m|^(\$\{SITE_PERL}/.*)$|) { &perror("WARN", $file, -1, "dependency to $1 ". "listed in $j. consider using p5-Example-Package-Name>=0. See ". "http://www.freebsd.org/doc/en/books/porters-handbook/using-perl.html". " for more details."); } # check USES=iconv if ($m{'dep'} =~ /^(iconv\.\d+)$/) { &perror("WARN", $file, -1, "dependency to $1 ". "listed in $j. consider using ". "USES[+]=iconv."); } # check USES=gettext if ($m{'dep'} =~ /^(intl\.\d+)$/) { &perror("WARN", $file, -1, "dependency to $1 ". "listed in $j. consider using ". "USES[+]=gettext."); } # check USES=gmake if ($m{'dep'} =~ /^(gmake|\$\{GMAKE})$/) { &perror("WARN", $file, -1, "dependency to $1 ". "listed in $j. consider using ". "USES[+]=gmake."); } my %udeps = ( 'bison' => 'bison', 'fmake' => 'fmake', ); foreach my $udep (keys %udeps) { if ($m{'dep'} =~ /^$udep/) { &perror("WARN", $file, -1, "dependency to $udep ". "listed in $j. consider using ". "USES[+]=$udeps{$udep}."); } } # check USE_QT if ($m{'dep'} =~ /^(qt\d)+$/) { &perror("WARN", $file, -1, "dependency to $1 ". "listed in $j. consider using ". "USE_QT."); } # check LIBLTDL if ($m{'dep'} =~ /^(ltdl\.\d)+$/) { &perror("WARN", $file, -1, "dependency to $1 ". "listed in $j. consider using ". "USE_LIBLTDL."); } # check GHOSTSCRIPT if ($m{'dep'} eq "gs") { &perror("WARN", $file, -1, "dependency to gs ". "listed in $j. consider using ". "USE_GHOSTSCRIPT(_BUILD|_RUN)."); } # check for PREFIX if ($m{'dep'} =~ /\$\{PREFIX}/) { &perror("FATAL", $file, -1, "\${PREFIX} must not be ". "contained in *_DEPENDS. ". "use \${LOCALBASE} instead."); } # Check for direct dependency on apache. if ($m{'dep'} =~ /\/www\/apache\d*\//) { &perror("FATAL", $file, -1, "do not depend on any apache ". "port in *_DEPENDS directly. ". "Instead use USE_APACHE=VERSION, where VERSION can be ". "found in \${PORTSDIR}/Mk/Uses/apache.mk."); } # Check for over-specific shared library dependencies if ($j eq 'LIB_DEPENDS' && $m{'dep'} =~ m/(\.\d+$)/) { &perror("WARN", $file, -1, "$j don't specify the " . "ABI version number $1 in $m{'dep'} unless it is " . "really necessary."); } # Check for old-style LIB_DEPENDS if ($j eq 'LIB_DEPENDS' && $m{'dep'} !~ m/^lib.*\.so$/) { &perror("WARN", $file, -1, "$j the new format is ". "libFOO.so (e.g., lib$m{'dep'}.so)."); } # Check port dir existence $k = $ENV{'PORTSDIR'}.'/'.$m{'dir'}; if (! -d $k) { &perror("WARN", $file, -1, "no port directory $k ". "found, even though it is ". "listed in $j."); } else { print "OK: port directory $k found.\n" if ($verbose); } # Check for relative path if ($k =~ /\/\.\.\//) { &perror("FATAL", $file, -1, "use absolute path". "instead of $k in *_DEPENDS."); } else { print "OK: path for port directory $k is absolute.\n" if ($verbose); } } } } # # Makefile # sub checkmakefile { my($file) = @_; my($rawwhole, $whole, $idx, @sections); my($i, $j, $k, $l); my @cat = (); my $has_lang_cat = 0; my $port_lang = ''; my $tmp; my $bogusdistfiles = 0; my @varnames = (); my($portname, $portversion, $distfiles, $distversionprefix, $distversion, $distversionsuffix, $distname, $extractsufx) = ('') x 8; my $masterport = 0; my $slaveport = 0; my $use_gnome_hack = 0; my $use_java = 0; my $use_ant = 0; my($realwrksrc, $wrksrc, $nowrksubdir) = ('', '', ''); my(@mman, @pman); my(@aopt, @mopt, @opt); my($pkg_version, $versiondir, $versionfile) = ('', '', ''); my $useindex = 0; my %deprecated = (); my @deplist = (); my %autocmdnames = (); my $pre_mk_line = 0; my $options_mk_line = 0; my $docsused = 0; my $optused = 0; my $desktop_entries = ''; my $masterdir = $makevar{MASTERDIR}; if ($masterdir ne '' && $masterdir ne $makevar{'.CURDIR'}) { $slaveport = 1; } open(IN, "< $file") || return 0; $rawwhole = ''; $tmp = 0; while () { if ($_ =~ /[ \t]+\n?$/) { &perror("WARN", $file, $., "whitespace before ". "end of line."); } if ($_ =~ /^ /) { # 8 spaces here! &perror("WARN", $file, $., "use tab (not space) to make ". "indentation"); } if ($usetabs) { if (m/^[A-Za-z0-9_-]+.?=\t*? \t*?/) { if (m/[?+]=/) { &perror("WARN", $file, $., "use a tab (not space) after a ". "variable name"); } else { &perror("FATAL", $file, $., "use a tab (not space) after a ". "variable name"); } } } $rawwhole .= $_; } close(IN); # # whole file: blank lines. # $whole = "\n" . $rawwhole; study $whole; print "OK: checking contiguous blank lines in $file.\n" if ($verbose); $i = "\n" x ($contblank + 2); if ($whole =~ /$i/) { my $lineno = &linenumber($`); &perror("FATAL", $file, $lineno, "contiguous blank lines ". "(> $contblank lines) found."); } # # whole file: header # my @lines = split("\n", $whole); print "OK: checking header in $file.\n" if ($verbose); if ($lines[1] =~ /^# (?:New )?[Pp]orts collection [mM]akefile/) { &perror("FATAL", $file, 1, "old style headers found."); } elsif ($lines[1] =~ /^# \$$rcsidstr[:\$]/) { &perror("FATAL", $file, 1, "\$$rcsidstr\$ is deprecated in Git."); } elsif ($lines[1] =~ /^# Created by: \S/) { if ($lines[2] =~ /^# \$$rcsidstr[:\$]/) { &perror("FATAL", $file, 2, "\$$rcsidstr\$ is deprecated in Git."); } if ($lines[2] !~ /^$/) { #&perror("FATAL", $file, 2, "do not add extra ". # "empty comments after header."); } # special case for $rcsidsrt\nMCom: } elsif ($lines[1] =~ /^#\s+\$MCom[:\$]/ and $lines[2] =~ /^$/) { # DO NOTHING } # # whole file: $(VARIABLE) # if ($parenwarn) { print "OK: checking for \$(VARIABLE).\n" if ($verbose); if ($whole =~ /[^\$]\$\([\w\d]+\)/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "use \${VARIABLE}, instead of ". "\$(VARIABLE)."); } } # # whole file: empty(${VARIABLE}) # if ($parenwarn) { print "OK: checking for empty(\${VARIABLE}).\n" if ($verbose); if ($whole =~ /empty\(\$\{[\w\d]+/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "use empty(VARIABLE), instead of ". "empty(\${VARIABLE})."); } } # # whole file: use of != # print "OK: checking for use of !=.\n" if ($verbose); if ($whole =~ /^[\w\d_]+\!=/m) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "use of != in assignments is almost ". "never a good thing to do. Try to avoid using them. See ". "http://lists.freebsd.org/pipermail/freebsd-ports/2008-July/049777.html ". "for some helpful hints on what to do instead."); } # # whole file: use of .elseif # print "OK: checking for use of .elseif.\n" if ($verbose); if ($whole =~ /^\.\s*else\s*if/m) { my $lineno = &linenumber($`); &perror("FATAL", $file, $lineno, "use of .elseif (or .else if) is not ". "supported in all versions of FreeBSD. Use .elif instead."); } # # whole file: use of @${INSTALL_foo} # print "OK: checking for use of muted INSTALL_ commands.\n" if ($verbose); if ($whole =~ /^\s+\@\$\{INSTALL_/m) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "do not use muted INSTALL_foo ". "commands (i.e., those that start with '\@'). These should be ". "printed."); } # # checking for use of ${ENV} # print "OK: checking for use of \${ENV} instead of \${SETENV}.\n" if ($verbose); if ($whole =~ /\$\{ENV}/m) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "most uses of \${ENV} should really ". "be \${SETENV} to avoid strange behaviors in sh(1)."); } # # whole file: use of IGNOREFILES # print "OK: checking for use of IGNOREFILES.\n" if ($verbose); if ($whole =~ /\nIGNOREFILES.?=/m) { my $lineno = &linenumber($`); &perror("FATAL", $file, $lineno, "IGNOREFILES considered unsafe and ". "not supported anymore."); } # # whole file: use of PLIST_DIRSTRY # print "OK: checking for use of PLIST_DIRSTRY.\n" if ($verbose); if ($whole =~ /\nPLIST_DIRSTRY.?=/m) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "PLIST_DIRSTRY is deprecated. Please ". "use PLIST_DIRS instead."); } # # whole file: PLIST_FILES and PLIST_DIRS # print "OK: checking PLIST_FILES and PLIST_DIRS.\n" if ($verbose); my $python_plist = 0; if ($makevar{USE_PYTHON} && $makevar{USE_PYTHON} =~ /\bautoplist\b/) { $python_plist = 1; } if ($whole =~ /\nPLIST_FILES.?=/ || $whole =~ /\nPLIST_DIRS.?=/) { if (-f 'pkg-plist') { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "You may remove pkg-plist ". "if you use PLIST_FILES and/or PLIST_DIRS."); } my @plist_files = split(/\s+/, $makevar{PLIST_FILES}); foreach my $plist_file (@plist_files) { if ($plist_file =~ m|lib[^\/]+\.so(\.\d+)?$| && $makevar{USE_LDCONFIG} eq '') { &perror("WARN", "", -1, "PLIST_FILES: installing shared libraries, ". "please define USE_LDCONFIG as appropriate"); } if ($plist_file =~ m|\.omf$| && $makevar{INSTALLS_OMF} eq '') { &perror("WARN", "", -1, "PLIST_FILES: installing OMF files, ". "please define INSTALLS_OMF (see the FreeBSD GNOME ". "porting guide at ". "http://www.FreeBSD.org/gnome/docs/porting.html ". "for more details)"); } if ($plist_file =~ m|\.core$| && $plist_file !~ /^\@/) { &perror("WARN", "", -1, "PLIST_FILES: this port installs a file which ". "ends in \".core\". This file may be deleted if ". "daily_clean_disks_enable=\"YES\" in /etc/periodic.conf. ". "If possible, install this file with a different name."); } if ($plist_file =~ m|^share/icons/.*/| && $makevar{INSTALLS_ICONS} eq '' && needs_installs_icons()) { &perror("WARN", "", -1, "PLIST_FILES: installing icons, ". "please define INSTALLS_ICONS as appropriate"); } if ($plist_file =~ /%%[\w_\d]+%%/) { &perror("FATAL", "", -1, "PLIST_FILES: files cannot contain ". "%%FOO%% variables. Use make variables and logic instead"); } } } # # whole file: USE_* and others variables used too late # my @options_early = qw( OPTIONS_DEFAULT OPTIONS_DEFINE OPTIONS_EXCLUDE OPTIONS_GROUP.*? OPTIONS_MULTI.*? OPTIONS_RADIO.*? OPTIONS_SINGLE.*? ); pos($whole) = 0; if ($whole =~ /^\.include\s+$/gm) { # Remember position $pre_mk_line = &linenumber($`) + 1; print "OK: checking for USE_* used too late.\n" if ($verbose); my @use_early = qw( BZIP2 GNUSTEP IMAKE KDE(?:BASE|LIBS)_VER (?:LIB)?RUBY LINUX_PREFIX OPENSSL PYTHON QT2? QT_VER X_PREFIX ZIP ); my @other_early = qw( EMACS_PORT_NAME ); my $earlypattern = join('|', 'USE_(?:'.join('|', @use_early).')', @other_early, @options_early); while ($whole =~ /^($earlypattern)[+?:!]?=/gmo) { my $lineno = &linenumber($`); &perror("FATAL", $file, $lineno, "$1 is set after ". "including bsd.port.pre.mk."); } } # # whole file: check OPTIONS # print "OK: checking OPTIONS.\n" if ($verbose); pos($whole) = 0; if ($whole =~ /^\.include\s+$/gm) { # Remember position $options_mk_line = &linenumber($`) + 1; } pos($whole) = 0; if ($whole =~ /^\.include\s+$/gm) { my $earlypattern = join('|', @options_early); while ($whole =~ /^($earlypattern)[+?]?=/gmo) { my $lineno = &linenumber($`); &perror("FATAL", $file, $lineno, "$1 is set after ". "including bsd.port.options.mk."); } } pos($whole) = 0; foreach my $i ("OPTIONS_RADIO","OPTIONS_SINGLE", "OPTIONS_MULTI","OPTIONS_GROUP") { foreach my $j (split(/\s+/, $makevar{$i})) { if ($j) { my @ocount = split(/\s+/, get_makevar("${i}_${j}")); if (!scalar(@ocount)) { &perror("FATAL", $file, -1, "Description for ${i}_${j} does not exist"); } else { push @aopt, @ocount; } } } } my @aropt = (); while ($whole =~ /^OPTIONS_DEFINE_[\d\w]+(.)=\s*(.+)$/mg) { push @aropt, split(/\s+/, $2); } @opt = split(/\s+/, $makevar{OPTIONS_DEFINE}); pos($whole) = 0; while ($whole =~ /PORT_OPTIONS:M(\w+)/mg) { push @mopt, $1; my $lineno = &linenumber($`) + 1; &perror("FATAL", $file, $lineno, "PORT_OPTIONS:M$1 is used before ". "including bsd.port.pre.mk or bsd.port.options.mk.") if ($optused && $lineno < $pre_mk_line && $lineno < $options_mk_line); } my @options_helpers = qw( __DUMMY__ ALL_TARGET BUILD_DEPENDS EXTRACT_DEPENDS FETCH_DEPENDS LIB_DEPENDS PKG_DEPENDS RUN_DEPENDS CATEGORIES CFLAGS CMAKE_OFF CMAKE_ON CMAKE_BOOL_OFF CMAKE_BOOL CONFIGURE_ENABLE CONFIGURE_ENV CONFIGURE_OFF CONFIGURE_ON CONFIGURE_WITH CPPFLAGS CXXFLAGS DISTFILES INSTALL_TARGET LDFLAGS MAKE_ARGS MAKE_ENV EXTRA_PATCHES PATCHFILES PATCH_DEPENDS PATCH_SITES PLIST_DIRS PLIST_DIRSTRY PLIST_FILES QMAKE_OFF QMAKE_ON USE USES VARS VARS_OFF ); my $m = join("|", @options_helpers); if ($makevar{OPTIONS_SUB}) { if ($makevar{PLIST_FILES}) { foreach my $i (split(/\s+/, $makevar{PLIST_FILES})) { while ($i =~ /\%\%([^%]+)\%\%/g) { push @popt, $1; } } } if ($makevar{PLIST_DIRS}) { foreach my $i (split(/\s+/, $makevar{PLIST_DIRS})) { while ($i =~ /\%\%([^%]+)\%\%/g) { push @popt, $1; } } } if (-f 'pkg-plist') { open(PL, 'pkg-plist'); my @pcontents = ; close(PL); foreach my $i (@pcontents) { while ($i =~ /\%\%([^%]+)\%\%/g) { push @popt, $1; } } } # special cases for PORTDOCS/PORTEXAMPLES push @popt, "DOCS" if $makevar{PORTDOCS}; push @popt, "EXAMPLES" if $makevar{PORTEXAMPLES}; # uniq(@popt) my %seen = (); @popt = grep { !$seen{$_}++ } @popt; } foreach my $i (@popt) { if ($i eq 'PORTDOCS') { if (!grep(/^DOCS$/, @opt)) { &perror("FATAL", $file, -1, "PORTDOCS appears in plist ". "but DOCS is not listed in OPTIONS_DEFINE."); } } elsif ($i eq 'PORTEXAMPLES') { if (!grep(/^EXAMPLES$/, @opt)) { &perror("FATAL", $file, -1, "PORTEXAMPLES appears in plist ". "but EXAMPLES is not listed in OPTIONS_DEFINE."); } } } my %seen_opts = (); foreach my $i ((@opt, @aopt, @aropt)) { # skip global options next if ($i eq 'DOCS' or $i eq 'NLS' or $i eq 'EXAMPLES' or $i eq 'IPV6' or $i eq 'X11' or $i eq 'DEBUG'); if (!$seen_opts{$i}) { $seen_opts{$i}++; my $odescr = &get_makevar("${i}_DESC"); if ($odescr eq "" && $whole !~ /^${i}_DESC.?=/m) { &perror("FATAL", $file, -1, "OPTION $i does not have a description (${i}_DESC)."); } } if (!grep(/^$i$/, (@mopt, @popt))) { if ($whole !~ /\n${i}_($m)(_\w+)?(.)?=[^\n]+/ and $whole !~ /\n[-\w]+-${i}-(on|off):\n/) { my $found_opt_use = 0; foreach my $oarg ('BUILD_DEPENDS', 'RUN_DEPENDS', 'LIB_DEPENDS') { my $oarg_var = &get_makevar("${i}_${oarg}"); if ($oarg_var ne "") { $found_opt_use = 1; last; } } if (!$found_opt_use) { if (!$slaveport) { &perror("WARN", $file, -1, "$i is listed in ". "OPTIONS_DEFINE, but no PORT_OPTIONS:M$i appears."); } else { &perror("WARN", $file, -1, "$i is listed in ". "OPTIONS_DEFINE, but no PORT_OPTIONS:M$i appears ". "in this slave Makefile. Make sure it appears in ". "the master's Makefile."); } } } } } foreach my $i (@mopt) { if (!grep(/^$i$/, @opt, @aopt, @aropt)) { # skip global options next if ($i eq 'DOCS' or $i eq 'NLS' or $i eq 'EXAMPLES' or $i eq 'IPV6' or $i eq 'X11'); &perror("WARN", $file, -1, "$i appears in PORT_OPTIONS:M, ". "but is not listed in OPTIONS_DEFINE."); } } # # whole file: check DESKTOP_ENTRIES for ${TRUE}/${FALSE} # print "OK: checking DESKTOP_ENTRIES for \${TRUE}/\${FALSE}.\n" if ($verbose); $desktop_entries = &get_makevar_raw('DESKTOP_ENTRIES'); if ($desktop_entries =~ /\$\{TRUE}/ or $desktop_entries =~ /\$\{FALSE}/ or $desktop_entries =~ /\"true\"/ or $desktop_entries =~ /\"false\"/) { &perror("FATAL", $file, -1, "Use true/false (without quotes) instead of \${TRUE}/\${FALSE} in DESKTOP_ENTRIES."); } # # whole file: USE_* as a user-settable option # print "OK: checking for USE_* as a user-settable option.\n" if ($verbose); while ($whole =~ /\n\s*\.\s*(?:el)?if[^\n]*?\b(\w*USE_)(\w+)(?![^\n]*\n#?\.error)/g) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "is $1$2 a user-settable option? ". "Consider using WITH_$2 instead.") if ($1.$2 ne 'USE_GCC' && $1.$2 ne 'USE_LDCONFIG32'); } # # whole file: check for use of *_CMAKE_ARGS # print "OK: checking for use of *_CMAKE_ARGS instead of *_CMAKE_ON|OFF.\n" if ($verbose); if ($whole =~ /\n([\w\d]+)_CMAKE_ARGS/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "Use $1_CMAKE_ON or $1_CMAKE_OFF instead ". "of $1_CMAKE_ARGS. The former macros will automatically update ". "CMAKE_ARGS."); } # # whole file: check that CMAKE_BOOL just has words # print "OK: checking that *_CMAKE_BOOL only contains words.\n" if ($verbose); if ($whole =~ /\n([\w\d]+)_CMAKE_BOOL[?+:]?=([^\n]+)\n/) { my $lineno = &linenumber($`); my $o = $1; if ($2 =~ /-D/) { &perror("FATAL", $file, $lineno, "Only bare words can be used for ". "${o}_CMAKE_BOOL. The -D flag will be added automatically."); } } print "OK: checking that *CMAKE* co-occurs with *USES+=cmake.\n" if ($verbose); while ($whole =~ /\n([\w\d]+_)?CMAKE_(ARGS|BOOL|BOOL_ON|BOOL_OFF|OFF|ON)\b/g) { my $lineno = &linenumber($`); my $o = $1; my $found_cmake = 0; unless ($makevar{USES} =~ /\b(cmake\b|cmake:)/) { $o = "" unless ($o); &perror("FATAL", $file, $lineno, "${o}CMAKE_$2 is set without USES+=cmake"); } } # # whole file: NO_CHECKSUM # # XXX Don't compress newlines since it messes up line number calculation. #$whole =~ s/\n#[^\n]*/\n/g; #$whole =~ s/\n\n+/\n/g; print "OK: checking NO_CHECKSUM.\n" if ($verbose); if ($whole =~ /\nNO_CHECKSUM/) { my $lineno = &linenumber($`); &perror("FATAL", $file, $lineno, "NO_CHECKSUM is a user ". "variable and is not to be set in a port's Makefile."); } # # whole file: MACHINE_ARCH # print "OK: checking MACHINE_ARCH.\n" if ($verbose); if ($whole =~ /\nMACHINE_ARCH/) { my $lineno = &linenumber($`); &perror("FATAL", $file, $lineno, "MACHINE_ARCH should never be ". "overridden."); } # # whole file: DEPRECATED # print "OK: checking DEPRECATED.\n" if ($verbose); if ($whole =~ /\nDEPRECATED[+?]?=[ \t]*"/ && $whole !~ /\nDEPRECATED[+?]?=[ \t]*"\$\{BROKEN\}"/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "DEPRECATED messages should not ". "be quoted unless they are exactly \"\${BROKEN}\"."); } if ($whole =~ /\nDEPRECATED[+?]?=[^"]*\$\{BROKEN\}/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "\"\${BROKEN}\" must be quoted ". "when it is the source of DEPRECATED."); } # # whole file: BROKEN et al. # my ($var); foreach $var (qw(IGNORE BROKEN COMMENT FORBIDDEN MANUAL_PACKAGE_BUILD NO_CDROM NO_PACKAGE RESTRICTED)) { print "OK: checking ${var}.\n" if ($verbose); if ($whole =~ /\n${var}[+?]?=[ \t]+"/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "${var} messages should not ". "be quoted."); } } if ($makevar{COMMENT} =~ /^An? / || $makevar{COMMENT} =~ /^The /) { &perror("WARN", $file, -1, "COMMENT is not supposed to begin with ". "'A ', 'An ', or 'The '."); } if ($whole =~ /\nIGNORE[+?]?=[ \t]+[^a-z \t]/ || $whole =~ /^IGNORE[+?]?=[ \t]+.*\.$/m) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "IGNORE messages should begin ". "with a lowercase letter and end without a period."); } if ($whole =~ /\nBROKEN[+?]=[ \t]+[^a-z \t]/ || $whole =~ /^BROKEN[+?]?=[ \t]+.*\.$/m) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "BROKEN messages should begin ". "with a lowercase letter and end without a period."); } # # whole file: PKGNAME # print "OK: checking PKGNAME.\n" if ($verbose); if ($whole =~ /\nPKGNAME.?=/) { my $lineno = &linenumber($`); &perror("FATAL", $file, $lineno, "PKGNAME is obsoleted by PORTNAME, ". "PORTVERSION, PKGNAMEPREFIX and PKGNAMESUFFIX."); } # # whole file: MAKE_JOBS_UNSAFE # print "OK: checking for MAKE_JOBS_UNSAFE in combination with NO_BUILD.\n" if ($verbose); if ($whole =~ /\nMAKE_JOBS_UNSAFE.?=/) { my $matched = $1; if ($whole =~ /\nNO_BUILD.?=/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "MAKE_JOBS_UNSAFE should not ". "be used in combination with NO_BUILD. You ". "should remove MAKE_JOBS_UNSAFE from your Makefile."); } } # # whole file: Check if some macros are sorted # my @macros_to_sort = qw( ONLY_FOR_ARCHS NOT_FOR_ARCHS ); print "OK: checking to see if certain macros are sorted.\n" if ($verbose); foreach my $sorted_macro (@macros_to_sort) { while ($whole =~ /\n$sorted_macro.?=\s*([^#\n]+)(#.*)?\n/g) { my $lineno = &linenumber($`); my $srex = $1; $srex =~ s/\s+$//; my @smacros = sort(split / /, $srex); if (join(" ", @smacros) ne $srex) { &perror("WARN", $file, $lineno, "the arguments to $sorted_macro ". "are not sorted. Please consider sorting them."); } } } # # whole file: USE_GNOME=pkgconfig # print "OK: checking for USE_GNOME=pkgconfig.\n" if ($verbose); if ($makevar{USE_GNOME} =~ /pkgconfig/) { &perror("WARN", $file, -1, "USE_GNOME=pkgconfig is now obsolete. ". "Use USES[+]=pkgconfig instead."); } # # whole file: using INSTALLS_ICONS when it is not wanted # if (!($makevar{INSTALLS_ICONS} eq '') && !needs_installs_icons()) { &perror("WARN", $file, -1, "INSTALLS_ICONS is set, but should not be."); } # # whole file: EXPIRATION_DATE # print "OK: checking for valid EXPIRATION_DATE.\n" if ($verbose); my $edate; if (($edate) = ($whole =~ m/\nEXPIRATION_DATE\??=[ \t]*([^\n]*)\n/)) { my $lineno = &linenumber($`); my $ndate = $edate; if ($ndate eq '' || length $ndate < 10) { $ndate = '0000-00-00'; } if ($ndate ne strftime("%Y-%m-%d", 0, 0, 0, substr($ndate, 8, 2), substr($ndate, 5, 2) - 1, substr($ndate, 0, 4) - 1900)) { &perror("FATAL", $file, $lineno, "EXPIRATION_DATE ($edate) is ". "either not in YYYY-MM-DD format or it is not a valid ". "date."); } } # # whole file: IS_INTERACTIVE/NOPORTDOCS|PORT_OPTIONS:MDOCS # print "OK: checking IS_INTERACTIVE.\n" if ($verbose); if ($whole =~ /\nIS_INTERACTIVE/) { if ($whole !~ /defined\((BATCH|FOR_CDROM)\)/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "use of IS_INTERACTIVE ". "discouraged. provide batch mode by using BATCH and/or ". "FOR_CDROM."); } } print "OK: checking for use of PORT_OPTIONS:MDOCS.\n" if ($verbose); if ($sharedocused && $whole =~ /PORT_OPTIONS:MDOCS/) { $docsused++; } print "OK: checking for use of NOPORTDOCS.\n" if ($verbose); if ($whole =~ /defined\s*\(?NOPORTDOCS\)?/ || $whole =~ /def\s*\(?NOPORTDOCS\)?/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "NOPORTDOCS is deprecated. Please ". "use PORT_OPTIONS:MDOCS instead."); } print "OK: checking for use of NOPORTEXAMPLES.\n" if ($verbose); if ($whole =~ /defined\s*\(?NOPORTEXAMPLES\)?/ || $whole =~ /def\s*\(?NOPORTEXAMPLES\)?/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "NOPORTEXAMPLES is deprecated. Please ". "use PORT_OPTIONS:MEXAMPLES instead."); } if ($sharedocused && $whole !~ /defined\s*\(?NOPORTDOCS\)?/ && $whole !~ /def\s*\(?NOPORTDOCS\)?/) { if ($docsused == 1 && $whole !~ m#(\$[\{\(]PREFIX[\}\)]|$localbase)/share/doc/#) { &perror("WARN", $file, -1, "you should only use \".if \${PORT_OPTIONS:MDOCS}\" to wrap ". "installation of files into $localbase/share/doc if the". " collection of files is large and it takes considerable time". " to copy."); } } else { $docsused++; } if ($docsused > 1) { &perror("FATAL", $file, -1, "Both NOPORTDOCS and PORT_OPTIONS:MDOCS are found. ". "Remove one or another."); } print "OK: checking for use of NOPORTDOCS.\n" if ($verbose); if ($whole =~ /NOPORTDOCS/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "NOPORTDOCS found. Consider ". "using PORT_OPTIONS:MDOCS."); } # # whole file: check for USES[+]=gettext # print "OK: checking for USES=gettext without PORT_OPTIONS:MNLS.\n" if ($verbose); if ($makevar{USES} =~ /\bgettext\b/ && $whole !~ /PORT_OPTIONS:MNLS/ && $whole !~ /NLS_USES=.*\bgettext\b/) { &perror("WARN", $file, -1, "Consider adding support for a NLS ". "knob to conditionally disable gettext support."); } # # whole file: check for deprecated commands # print "OK: checking for deprecated macros.\n" if $verbose; %deprecated = ( USE_RCORDER => 'USE_RC_SUBR', ); @deplist = (\%deprecated); for my $dlst (@deplist) { my $hurl = $dlst->{'__HELP__'}; foreach my $depmacro (keys %{$dlst}) { if ($whole =~ /\n($depmacro)[+?:!]?=/) { my $derror = "$depmacro is ". "deprecated, use $dlst->{$1} instead"; if (defined($hurl)) { $derror .= " (see $hurl for more details)"; } &perror("FATAL", $file, -1, $derror); } } } # # whole file: DOS line endings # print "OK: checking for DOS line ending removal.\n" if ($verbose); if ($whole =~ / / || $whole =~ /:cntrl:/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "Possible manual removal of DOS ". "line endings found. Consider defining USES=dos2unix instead."); } # # whole file: direct use of command names # my %cmdnames = (); print "OK: checking direct use of command names.\n" if ($verbose); foreach my $i (qw( awk basename brandelf cat chmod chown cp cpio dialog dirname egrep expr false file find gmake grep gzcat ldconfig ln md5 mkdir mv objcopy paste patch pax perl printf rm rmdir ruby sed sdl-config sh sort sysctl touch tr which xargs xmkmf )) { $cmdnames{$i} = "\$\{\U$i\E\}"; } $cmdnames{'echo'} = '${ECHO_CMD} or ${ECHO_MSG}'; $cmdnames{'env'} = '${SETENV}'; $cmdnames{'gunzip'} = '${GUNZIP_CMD}'; $cmdnames{'gzip'} = '${GZIP_CMD}'; $cmdnames{'install'} = '${INSTALL_foobar}'; $cmdnames{'python'} = '${PYTHON_CMD}'; $cmdnames{'sdl-config'} = '${SDL_CONFIG}'; $cmdnames{'strip'} = '${STRIP_CMD}'; $cmdnames{'unzip'} = '${UNZIP_CMD}'; $cmdnames{'pkg_create'} = '${PKG_CMD}'; foreach my $i (qw(aclocal autoconf autoheader automake autoreconf autoupdate autoscan ifnames libtoolize)) { $autocmdnames{$i} = "\$\{" . ( ( $i !~ /auto|aclocal/ ) ? "AUTO" : "" ) . "\U$i\E\}"; } # # ignore parameter string to echo command. # note that we leave the command as is, since we need to check the # use of echo itself. $j = $whole; $j =~ s/([ \t][\@\-]{0,2})(echo|\$[\{\(]ECHO[\}\)]|\$[\{\(]ECHO_MSG[\}\)])[ \t]+(?:"(?:\\'|\\"|[^"])*"|'(?:\\'|\\"|[^'])*')[ \t]*;?(\n?)/$1$2;$3/g; #" # ignore variables names in .for loops, but not what's at the end # of the for loop $j =~ s/(\.for +)([^ ]*)( .*)/$1$3/; foreach my $i (keys %cmdnames) { # XXX This is a hack. Really, we should break $j up into individual # lines, and go through each one. while ($j =~ /^(.*\b$i\b.*)$/gm) { my $lineno = &linenumber($`); my $curline = $1; my $dte_test = $curline; $dte_test =~ s/^\s+//g; if ($desktop_entries =~ /\Q$dte_test\E$/) { next; } if ($curline =~ /(?:^|\s)[\@\-]{0,2}$i(?:$|\s)/ && $curline !~ /^[A-Z]+_TARGET[?+]?=[^\n]+$i/m && $curline !~ /^[A-Z]+_INSTALL_TARGET[?+]?=[^\n]+$i/m && $curline !~ /^IGNORE(_[\w\d]+)?(.)?=[^\n]+$i/m && $curline !~ /^BROKEN(_[\w\d]+)?(.)?=[^\n]+$i/m && $curline !~ /^RESTRICTED(.)?=[^\n]+$i/m && $curline !~ /^NO_PACKAGE(.)?=[^\n]+$i/m && $curline !~ /^NO_CDROM(.)?=[^\n]+$i/m && $curline !~ /^MAINTAINER(.)?=[^\n]+$i/m && $curline !~ /^CATEGORIES(.)?=[^\n]+$i/m && $curline !~ /^(\w+)?USES(.)?=[^\n]+$i/m && $curline !~ /^WX_COMPS(.)?=[^\n]+$i/m && $curline !~ /^ONLY_FOR_ARCHS_REASON(_[\w\d]+)?(.)?=[^\n]+$i/m && $curline !~ /^NOT_FOR_ARCHS_REASON(_[\w\d]+)?(.)?=[^\n]+$i/m && $curline !~ /^SHEBANG_FILES(.)?=[^\n]+$i/m && $curline !~ /^[\w\d]+_OLD_CMD(.)?=[^\n]+$i/m && $curline !~ /^[A-Z0-9_]+_DESC=[^\n]+$i/m && $curline !~ /#.*?$i/m && $curline !~ /^\s*#.+$/m && $curline !~ /\$\{MAKE_CMD\}.*\binstall\b/m && $curline !~ /\-\-$i/m && $curline !~ /^COMMENT(.)?=[^\n]+$i/m) { &perror("WARN", $file, $lineno, "possible direct use of ". "command \"$i\" found. use ". "$cmdnames{$i} instead."); } } } foreach my $i (keys %autocmdnames) { # XXX Same hack as above. while ($j =~ /^(.*(\b$i\d*).*)$/gm) { my $lm = $1; my $sm = $2; my $lineno = &linenumber($`); if ($lm =~ /(^|\s+)[\@\-]{0,2}($i\d*)\b/ && $lm !~ /^[A-Z]+_TARGET[?+]?=[^\n]+($i\d*)/m && $lm !~ /^IGNORE(.)?=[^\n]+($i\d*)/m && $lm !~ /^BROKEN(.)?=[^\n]+($i\d*)/m && $lm !~ /^RESTRICTED(.)?=[^\n]+($i\d*)/m && $lm !~ /^NO_PACKAGE(.)?=[^\n]+($i\d*)/m && $lm !~ /^NO_CDROM(.)?=[^\n]+($i\d*)/m && $lm !~ /^MAINTAINER(.)?=[^\n]+($i\d*)/m && $lm !~ /^CATEGORIES(.)?=[^\n]+($i\d*)/m && $lm !~ /^USES(.)?=[^\n]+$i/m && $lm !~ /^[A-Z0-9_]+_DESC=[^\n]+($i\d*)/m && $lm !~ /^SHEBANG_FILES(.)?=[^\n]+($i\d*)/m && $lm !~ /^USE_AUTOTOOLS(.)?=[^\n]+($i\d*)/m && $lm !~ /^\s*#.+$/m && $lm !~ /^COMMENT(.)?=[^\n]+($i\d*)/m) { &perror("WARN", $file, $lineno, "possible direct use of ". "command \"$sm\" found. Use $autocmdnames{$i} ". "instead and set according USE_AUTOTOOLS= macro"); } } } if ($makevar{'USE_AUTOTOOLS'} =~ /\blibtool\b/) { &perror("WARN", $file, -1, "USE_AUTOTOOLS=libtool is deprecated. ". "Use USES=libtool instead."); } # # whole file: check for use of paths that have macro replacements # my %pathnames = (); print "OK: checking for paths that have macro replacements.\n" if ($verbose); $pathnames{'$\{PREFIX\}/share/java/classes'} = 'JAVADIR'; $pathnames{'$\{PREFIX\}/share/java'} = 'JAVASHAREDIR'; foreach my $i (keys %pathnames) { my $lineno = &linenumber($`); if ($j =~ m|$i|gm) { &perror("FATAL", $file, $lineno, "you should use ". "$pathnames{$i} rather than $i"); } } # # whole file: ${GZIP_CMD} -9 (or any other number) # print "OK: checking for compression arguments passed to \${GZIP_CMD}.\n" if ($verbose); if ($j =~ /\$\{GZIP_CMD}\s+-(\w+(\s+-)?)*(\d)/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "possible use of \"\${GZIP_CMD} -$3\" ". "found. \${GZIP_CMD} includes \"-\${GZIP}\" which ". "sets the compression level."); } # # whole file: ${CHMOD} used # print "OK: checking for \${CHMOD}.\n" if ($verbose); if ($j =~ /\n\s*\$\{CHMOD}/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "possible use of \"\${CHMOD}\" ". "found. Use @(owner,group,mode) syntax or \@owner/\@group ". "operators in pkg-plist instead."); } # # whole file: ${INSTALL} -o | -g used # print "OK: checking for \${INSTALL} -o | -g.\n" if ($verbose); if ($j =~ /\n\s*\$\{INSTALL}(.*-\b(o|g)\b.*)/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "possible use of \"\${INSTALL} -o | -g\" ". "found. Use @(owner,group,mode) syntax or \@owner/\@group ". "operators in pkg-plist instead."); } # # whole file: ${MKDIR} -p # print "OK: checking for \${MKDIR} -p.\n" if ($verbose); if ($j =~ /\$\{MKDIR}\s+-p/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "possible use of \"\${MKDIR} -p\" ". "found. \${MKDIR} includes ". "\"-p\" by default."); } # # check for use of ${FIND} ... ${XARGS} ${RM} # print "OK: checking for instances of \${FIND} ... \${XARGS} \${RM}.\n" if ($verbose); if ($j =~ /\$\{FIND\}.*\|.*\$\{XARGS\}.*\$\{RM\}/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "possible use of ". "\"\${FIND} ... \${XARGS} \${RM}\" when ". "\"\${FIND} ... -delete\" will work."); } # # whole file: ${MACHINE_ARCH} # print "OK: checking for instances of \${MACHINE_ARCH} being test.\n" if ($verbose); if ($j =~ /\$\{MACHINE_ARCH}\s*[!=]=/) { my $lineno = &linenumber($`); &perror("FATAL", $file, $lineno, "MACHINE_ARCH should never be tested ". "directly; use ARCH instead."); } # # whole file: full path name # &abspathname($whole, $file); # # whole file: SITE_PERL # print "OK: checking SITE_PERL.\n" if ($verbose); if ($whole =~ /\nSITE_PERL[?:]?=/) { my $lineno = &linenumber($`); &perror("FATAL", $file, $lineno, "use of SITE_PERL discouraged. ". "it is set in bsd.port.mk."); } # # whole file: ${LOCALBASE}/lib/perl5/site_perl # if ($j =~ m'\$\{(?:LOCALBASE|PREFIX)}/lib/perl5/site_perl') { my $lineno = &linenumber($`); if ($1 !~ /PREFIX/) { &perror("WARN", $file, $lineno, "possible use of \"\${LOCALBASE}/lib/perl5/site_perl\" ". "found. use \"\${SITE_PERL}\" instead."); } else { &perror("WARN", $file, $lineno, "possible use of \"\${PREFIX}/lib/perl5/site_perl\" ". "found. use \"\${PREFIX}/\${SITE_PERL_REL}\" instead."); } } # # whole file: check for misuse of STAGE with SITE_PERL and SITE_ARCH # if ($j =~ m'\$\{STAGEDIR}\$\{SITE_PERL}') { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "\${STAGEDIR}\${SITE_PERL} should be ". "replaced by \${STAGEDIR}\${PREFIX}/\${SITE_PERL_REL}."); } if ($j =~ m'\$\{STAGEDIR}\$\{SITE_ARCH}') { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "\${STAGEDIR}\${SITE_ARCH} should be ". "replaced by \${STAGEDIR}\${PREFIX}/\${SITE_ARCH_REL}."); } # # whole file: USE_GNOME check # if ($whole =~ /^USE_GNOME[?:]?=\s*(.*)$/m) { if ($1 =~ /gnomehack/) { $use_gnome_hack = 1; } } # # whole file: USE_KDE check # if ($whole =~ /^USE_KDE[?:]?=\s*(.*)$/m) { if ($makevar{USES} !~ /\bkde:[45]/) { my $lineno = &linenumber($`); &perror("WARN", $file, $lineno, "USE_KDE is defined without ". "defining USES=kde:[45]"); } } # # whole file: USES=pyqt:5 # if ($makevar{USES} =~ /\bpyqt:5/ && $whole !~ /^USE_PYQT[?:]?=\s(.*)$/m && $makevar{USE_PYQT} eq '') { &perror("WARN", $file, -1, "When USES=pyqt:5 is defined, you must also define ". "USE_PYQT=xxxx"); } # # whole file: USE_GCC checks # if ($whole =~ /^USE_GCC[?:]?=\s*([^\s#]*).*$/m) { my $lineno = &linenumber($`); my $gcc_val = $1; if ($gcc_val eq 'any' || $gcc_val eq 'yes') { # Just accept these two. } elsif ($gcc_val !~ /\+/) { &perror("WARN", $file, $lineno, "Setting a specific version for ". "USE_GCC should only be done as a last resort. Unless you ". "have confirmed this port does not build with later ". "versions of GCC, please use USE_GCC=$gcc_val+."); } } # # whole file: USE_JAVA check # if ($whole =~ /^USE_JAVA[?:]?=\s*(.*)$/m) { $use_java = 1; } # # whole file: USE_ANT check # if ($whole =~ /^USE_ANT[?:]?=\s*(.*)$/m) { $use_ant = 1; } # # whole file: USE_JAVA not defined, but other Java components are requested # if (!$use_java && ($use_ant || $whole =~ /^JAVA_VERSION[?:]?=\s*(.*)$/m || $whole =~ /^JAVA_OS[?:]?=\s*(.*)$/m || $whole =~ /^JAVA_VENDOR[?:]?=\s*(.*)$/m || $whole =~ /^JAVA_RUN[?:]?=\s*(.*)$/m || $whole =~ /^JAVA_BUILD[?:]?=\s*(.*)$/m)) { &perror("FATAL", $file, -1, "the port uses Java features, but USE_JAVA ". "is not defined"); } # # whole file: check for USE_ANT and USES=gmake both defined # if ($use_ant && $makevar{USES} =~ /\bgmake\b/) { &perror("WARN", $file, -1, "a port shall not define both USE_ANT ". "and USES[+]=gmake"); } # # whole file: check for USE_APACHE=yes # if ($whole =~ /^USE_APACHE[?:]?=\s*(yes)$/m) { &perror("FATAL", $file, -1, "Use USE_APACHE=VERSION ". "(where version can be found in \${PORTSDIR}/Mk/bsd.apache.mk) ". "instead of yes"); } # # whole file: check for WITH_APACHE\d+ # if ($whole =~ /WITH_APACHE\d+/) { &perror("FATAL", $file, -1, "Use WITH_APACHE=yes and .if ". "\${APACHE_VERSION} [==|<|>] 13|20|22|24"); } # # whole file: check for JAVA_BUILD and NO_BUILD # if ($whole =~ /^NO_BUILD[?:]?=\s*(.*)$/m && $whole =~ /^JAVA_BUILD[?:]?=\s*(.*)$/m) { &perror("FATAL", $file, -1, "JAVA_BUILD and NO_BUILD cannot be set ". "at the same time"); } # # whole file: check for reassignment of ECHO_MSG # if ($whole =~ /^ECHO_MSG[?:]?=\s*(.*)$/m) { &perror("FATAL", $file, -1, "Re-assigning ECHO_MSG can break ". "``make readme''. Consider using \${PRINTF} directly instead ". "for custom message output."); } # # whole file: check for --build, --mandir, and --infodir # when GNU_CONFIGURE # if ($makevar{GNU_CONFIGURE} ne '' && $makevar{CONFIGURE_ARGS} =~ /--(build|(man|info)dir)/) { &perror("WARN", $file, -1, "--build, --mandir, and --infodir ". "are not needed in CONFIGURE_ARGS as they are already set in ". "bsd.port.mk."); } # # whole file: check for redundant SHEBANG_LANGs # if ($whole =~ /^SHEBANG_LANG[?+]?=\s*(.*)$/m) { my $sh_lang = $1; my @shebang_langs = split(/\s+/, $makevar{SHEBANG_LANG} // ''); my %sh_seen = (); foreach my $shebang_lang (@shebang_langs) { if ($sh_seen{$shebang_lang}) { $sh_seen{$shebang_lang}++; } else { $sh_seen{$shebang_lang} = 1; } if ($sh_seen{$shebang_lang} > 1 && $sh_lang =~ /\b$shebang_lang\b/) { &perror("WARN", $file, -1, "$shebang_lang is already included in ". "SHEBANG_LANG. You should remove this from $file."); } } } # # whole file: CONFIGURE_ENV # if ($whole =~ /\nCONFIGURE_ENV[?:+]?=\s*([^\\\n]+(\\\n[^\\\n]+)*)/) { my $configure_env = $1; my $cflags = undef; my $cxxflags = undef; if ($configure_env =~ /\bCFLAGS="([^"]+)"/ || $configure_env =~ /\bCFLAGS='([^']+)'/ || $configure_env =~ /\bCFLAGS=(\S+)/) { $cflags = $1; } if ($configure_env =~ /\bCXXFLAGS="([^"]+)"/ || $configure_env =~ /\bCXXFLAGS='([^']+)'/ || $configure_env =~ /\bCXXFLAGS=(\S+)/) { $cxxflags = $1; } if (defined($cflags) || defined($cxxflags)) { &perror("WARN", $file, -1, "CFLAGS/CXXFLAGS are not needed in ". "CONFIGURE_ENV as they are already added there in bsd.port.mk."); } if ($makevar{GNU_CONFIGURE} ne '') { if ((defined($cflags) && $cflags =~ /-I/) || (defined($cxxflags) && $cxxflags =~ /-I/)) { &perror("WARN", $file, -1, "Consider passing include paths ". "to configure via the CPPFLAGS macro ". "(i.e. CPPFLAGS+=-I...)"); } } if (defined($cflags) && $cflags !~ /\$\{CFLAGS/) { &perror("FATAL", $file, -1, "CFLAGS are clobbered in ". "CONFIGURE_ENV. Alter CFLAGS in the Makefile with ". "CFLAGS+=... instead"); } if (defined($cxxflags) && $cxxflags !~ /\$\{CXXFLAGS/) { &perror("FATAL", $file, -1, "CXXFLAGS are clobbered in ". "CONFIGURE_ENV. Alter CXXFLAGS in the Makefile with ". "CXXFLAGS+=... instead"); } if ($configure_env =~ /(FC)=/ || $configure_env =~ /(F77)=/ || $configure_env =~ /(FFLAGS)=/) { &perror("FATAL", $file, -1, "$1 is already ". "passed in CONFIGURE_ENV via bsd.gcc.mk. If you need to ". "override the default value, alter $1 in the Makefile ". "instead with $1=..."); } if ($configure_env =~ /(\bCPPFLAGS)=/) { &perror("FATAL", $file, -1, "$1 is already ". "passed in CONFIGURE_ENV via bsd.port.mk. If you need to ". "override the default value, alter $1 in the Makefile ". "instead with $1+=..."); } if ($configure_env =~ /(\bLDFLAGS)=/) { &perror("FATAL", $file, -1, "$1 is already passed in ". "CONFIGURE_ENV via bsd.port.mk. If you need to ". "override the default value, alter $1 in the Makefile ". "instead with $1+=..."); } } # # whole file: *FLAGS # foreach my $f (qw(CFLAGS CXXFLAGS CPPFLAGS LDFLAGS)) { if ($whole =~ /^$f=/m) { &perror("WARN", $file, -1, "$f is overridden in the Makefile ". "clobbering a value possibly set by a user. Consider ". "using $f+=... if you want to add or $f:=\${$f:C/...//} ". "if you want to remove specific flags"); } } # # whole file: MAKE_ENV # if ($whole =~ /\nMAKE_ENV[?:+]?=\s*([^\\\n]+(\\\n[^\\\n]+)*)/) { my $make_env = $1; if ($make_env =~ /(CPPFLAGS)=/) { &perror("FATAL", $file, -1, "$1 is already ". "passed in MAKE_ENV via bsd.port.mk. If you need to ". "override the default value, alter $1 in the Makefile ". "instead with $1=..."); } } # # slave port check # if ($slaveport) { print "OK: slave port detected, checking for inclusion of $masterdir/Makefile.\n" if ($verbose); if ($whole =~ /^\.\s*include\s*[<"]bsd\.port(?:\.post)?\.mk[">]/m) { &perror("FATAL", $file, -1, "supposedly non-slave port with". " .CURDIR != MASTERDIR"); } elsif ($whole =~ /^\.\s*include\s*[<"]bsd\.port\.pre\.mk[">]/m) { &perror("FATAL", $file, -1, "slave ports may not include". " bsd.port.pre.mk"); } if ($whole !~ /\n\.include\s+"\$\{MASTERDIR\}\/Makefile"\s*$/s) { &perror("FATAL", $file, -1, "the last line of a slave port's Makefile has to be". ' .include "${MASTERDIR}/Makefile"'); } print "OK: checking master port in $masterdir.\n" if ($verbose); if (! -e "$masterdir/Makefile") { &perror("WARN", "", -1, "unable to locate master port in $masterdir"); } if ($whole !~ /^MASTERDIR=\s*\$\{\.CURDIR\}(?:\/\.\.){1,2}(?:\/[\w\@.+-]+){1,2}\s*$/m) { &perror("WARN", $file, -1, "slave ports must define MASTERDIR=". '${.CURDIR}/..(/../)/'); } } else { #$slaveport = 0; print "OK: non-slave port detected, checking for anything after bsd.port(.post).mk.\n" if ($verbose); if ($whole !~ /\n\.include\s+\s*$/s && $whole !~ /\n\.endif\s*$/s) { &perror("FATAL", $file, -1, "the last line of Makefile has to be". ' .include (or .endif in the case of a conditional)'); } if ($whole =~ /^MASTERDIR\s*[+?:!]?\s*=/m) { &perror("WARN", $file, -1, "non-slave ports may not define MASTERDIR"); } } # # break the makefile into sections. # $tmp = $rawwhole; $tmp =~ s/\\\n/ /g; # keep comment, blank line, comment in the same section # XXX: Take this out since it breaks some commenting; see PR240359. #$tmp =~ s/(#.*\n)\n+(#.*)/$1$2/g; @sections = split(/\n\n+/, $tmp); for ($i = 0; $i <= $#sections; $i++) { if ($sections[$i] !~ /\n$/) { $sections[$i] .= "\n"; } } $idx = 0; my @linestocheck = (); # check if all lines in the first section are comments if (grep(/^#/, split(/\n/, $sections[$idx])) == split(/\n/, $sections[$idx])) { # # section 1: comment lines. # print "OK: checking comment section of $file.\n" if ($verbose); @linestocheck = split("\n", < 0 && $cat[0] eq "java") { &perror("WARN", $file, -1, "save for ports directly related to the Java ". "language, porters are encouraged not to use ``java'' as ". "the main category for a port"); } if ($committer && $makevar{'.CURDIR'} =~ m/\Q${portsdir}\E\/([^\/]+)\/[^\/]+\/?$/) { if ($cat[0] ne $1 && $makevar{PKGCATEGORY} ne $1 ) { &perror("FATAL", $file, -1, "category \"$1\" must be listed first"); } } #MICHAEL: can these three lang cat checks be combined? # skip the first category specification if it's a language specific one. if (grep($_ eq $cat[0], @lang_cat)) { $has_lang_cat = 1; $port_lang = $lang_pref{$cat[0]}; shift @cat; } # skip further if more language specific ones follow. if (@cat && grep($_ eq $cat[0], @lang_cat)) { &perror("WARN", $file, -1, "multiple language specific categories detected. ". "are you sure?"); do { shift @cat; } while (@cat && grep($_ eq $cat[0], @lang_cat)); } # check x11 in CATEGORIES #MICHAEL: I don't understand this line if (2 <= @cat && $cat[1] eq "x11") { &perror("WARN", $file, -1, "only specific kind of apps should ". "specify \"x11\" in CATEGORIES. ". "Do you mean just USE_XORG? ". "Then remove \"x11\" from CATEGORIES."); } if (2 <= @cat) { # skip the first one that we know is _not_ language specific. shift @cat; # any language specific one after non language specific ones? foreach my $cat (@cat) { if (grep($_ eq $cat, @lang_cat)) { $has_lang_cat = 1; $port_lang = $lang_pref{$cat}; &perror("WARN", $file, -1, "when you specify multiple categories, ". "language specific category should come first."); } } } # check the URL if (($tmp =~ /\nMASTER_SITES[+?]?=[ \t]*([^\n]*)\n/ && $1 !~ /^[ \t]*$/) || ($makevar{MASTER_SITES} ne '')) { print "OK: seen MASTER_SITES, sanity checking URLs.\n" if ($verbose); my $urlseen = 0; my @sites = split(/\s+/, $1 // ''); my $ftphttp = 0; foreach my $i (@sites) { last if ($i =~ /^#/); if ($i =~ m#^\w+://#) { $urlseen = 1; $ftphttp = 1 if ($i =~ /^(ftp|http):/); &urlcheck($i, $file); unless (&is_predefined($i, $file)) { print "OK: URL \"$i\" ok.\n" if ($verbose); } } else { my $good_ms = 1; foreach my $mss (split(/\s+/, $makevar{MASTER_SITES_SUBDIRS})) { my ($ms, $sd) = split(/:/, $mss); if ($i =~ /^$ms/ && $i ne $ms) { my $ip = $i; $ip =~ s/^$ms\///; my $exp_sd = get_makevar($ip); if ($exp_sd eq $sd) { &perror("WARN", $file, -1, "typically when you specify magic site $ms ". "you do not need anything else as $sd is assumed"); $good_ms = 0; } } } if ($good_ms) { print "OK: non-URL \"$i\" ok.\n" if ($verbose); } # Assume variables contain an ftp/http site. $ftphttp = 1; } } } else { &perror("WARN", $file, -1, "no MASTER_SITES found. is it ok?"); } # check DISTFILES and related items. $distfiles = $1 if ($tmp =~ /\nDISTFILES[+?]?=[ \t]*([^\n]+)\n/); $portname = $makevar{PORTNAME}; $portversion = $makevar{PORTVERSION}; $distversionprefix = $makevar{DISTVERSIONPREFIX}; $distversion = $makevar{DISTVERSION}; $distversionsuffix = $makevar{DISTVERSIONSUFFIX}; $distname = $1 if ($tmp =~ /\nDISTNAME[+?]?=[ \t]*([^\n]+)\n/); $extractsufx = $1 if ($tmp =~ /\nEXTRACT_SUFX[+?]?=[ \t]*([^\n]+)\n/); # check bogus EXTRACT_SUFX. if ($extractsufx ne '') { print "OK: seen EXTRACT_SUFX, checking value.\n" if ($verbose); if ($distfiles ne '') { &perror("WARN", $file, -1, "no need to define EXTRACT_SUFX if ". "DISTFILES is defined."); } if ($extractsufx eq '.tar.gz') { &perror("WARN", $file, -1, "EXTRACT_SUFX is \".tar.gz.\" ". "by default. you don't need to specify it."); } if ($extractsufx =~ /^\.tar\.(bz2|lzma|xz|Z)$/) { my $ext = $1; $ext = 'bzip2' if ($ext eq 'bz2'); &perror("WARN", $file, -1, "EXTRACT_SUFX is \".tar.$ext\". ". "Please use USES=tar:$ext instead."); } if ($extractsufx =~ /^\.(tgz|tbz|txz)$/) { &perror("WARN", $file, -1, "EXTRACT_SUFX is \".$1\". ". "Please use USES=tar:$1 instead."); } if ($extractsufx eq '.zip') { &perror("WARN", $file, -1, "EXTRACT_SUFX is \".zip\" ". "You should use USES[+]=zip instead."); } } else { print "OK: no EXTRACT_SUFX seen, using default value.\n" if ($verbose); $extractsufx = '.tar.gz'; } print "OK: sanity checking PORTNAME/PORTVERSION/DISTVERSIONPREFIX/DISTVERSION/DISTVERSIONSUFFIX.\n" if ($verbose); if ($distname ne '') { my $exp_distname = $makevar{DISTNAME}; if ($exp_distname eq "$portname-$portversion") { &perror("WARN", $file, -1, "DISTNAME is \${PORTNAME}-\${PORTVERSION} by ". "default, you don't need to define DISTNAME."); } else { if ($exp_distname eq "$portname-$distversionprefix$distversion$distversionsuffix") { &perror("WARN", $file, -1, "DISTNAME is \${PORTNAME}-\${DISTVERSIONPREFIX}\${DISTVERSION}\${DISTVERSIONSUFFIX} by ". "default, you don't need to define DISTNAME."); } } if ($distname =~ /PORTREVISION/) { &perror("FATAL", $file, -1, "DISTNAME contains a reference to ". "PORTREVISION. You should only be using PORTVERSION"); } if ($distname =~ /PORTEPOCH/) { &perror("FATAL", $file, -1, "DISTNAME contains a reference to ". "PORTEPOCH. You should only be using PORTVERSION"); } } if ($portname =~ /^$re_lang_short/) { &perror("FATAL", $file, -1, "language prefix is automatically". " set by PKGNAMEPREFIX.". " you must remove it from PORTNAME."); } if ($portname =~ /([|<>=! ])/) { &perror("FATAL", $file, -1, "PORTNAME contains the illegal character \"$1\".". " You should modify \"$portname\"."); } elsif ($portname =~ /\$[\{\(].+[\}\)]/) { &perror("WARN", $file, -1, "using variable in PORTNAME.". " consider using PKGNAMEPREFIX and/or PKGNAMESUFFIX."); } elsif ($portname =~ /([^\w._@+-])/) { &perror("WARN", $file, -1, "using \"$1\" in PORTNAME.". " You should modify \"$portname\"."); } elsif ($portname =~ /-/ && $distname ne '') { &perror("WARN", $file, -1, "using hyphen in PORTNAME.". " consider using PKGNAMEPREFIX and/or PKGNAMESUFFIX."); } if ($portversion eq '' && $distversion eq '') { &perror("FATAL", $file, -1, "either PORTVERSION or DISTVERSION must be specified"); } if ($portversion =~ /^pl[0-9]*$/ || $portversion =~ /^[0-9]*[A-Za-z]?[0-9]*(\.[0-9]*[A-Za-z]?[0-9+]*)*$/) { print "OK: PORTVERSION \"$portversion\" looks fine.\n" if ($verbose); } elsif ($portversion =~ /^[^\-]*\$[{\(].+[\)}][^\-]*$/) { &perror("WARN", $file, -1, "using variable, \"$portversion\", as version ". "number"); } elsif ($portversion =~ /([-,_<>=! #*])/) { &perror("FATAL", $file, -1, "PORTVERSION must not contain \"$1\". ". "You should modify \"$portversion\"."); } else { &perror("FATAL", $file, -1, "PORTVERSION looks illegal. ". "You should modify \"$portversion\"."); } $pkg_version = $makevar{PKG_VERSION}; if ($makevar{CONFLICTS}) { print "OK: checking CONFLICTS.\n" if ($verbose); foreach my $conflict (split ' ', $makevar{CONFLICTS}) { `$pkg_version -T '$makevar{PKGNAME}' '$conflict'`; my $selfconflict = !$?; if ($selfconflict) { &perror("FATAL", "", -1, "Package conflicts with itself. ". "You should remove \"$conflict\" from CONFLICTS."); } } } $versiondir = $ENV{VERSIONDIR} // '/var/db/chkversion'; $versionfile = "$versiondir/VERSIONS"; $useindex = !-r "$versionfile"; $versionfile = "$portsdir/$makevar{INDEXFILE}" if $useindex; if (-r "$versionfile") { print "OK: checking if PORTVERSION is going backwards.\n" if ($verbose); open VERSIONS, "<$versionfile"; while () { my($origin, $version) = ('', ''); chomp; next if /^(#|$)/; if ($useindex) { ($version, $origin) = split /\|/; $origin =~ s,^.*/([^/]+/[^/]+)/?$,$1,; } else { ($origin, $version) = split; } if ($origin eq $makevar{PKGORIGIN}) { my $newversion = $makevar{PKGNAME}; my $oldversion = $version; my $result = ''; $newversion =~ s/^.*-//; $oldversion =~ s/^.*-//; $result = `$pkg_version -t '$newversion' '$oldversion'`; chomp $result; if ($result eq '<') { &perror("FATAL", $file, -1, "$makevar{PKGNAME} < $version. ". "Choose another PORTVERSION or bump PORTEPOCH."); # $backwards{$origin} = "$pkgname{$origin} < $version"; } last; } } close VERSIONS; } # if DISTFILES have only single item, it is better to avoid DISTFILES # and to use combination of DISTNAME and EXTRACT_SUFX (unless USE_GITHUB # or USE_GITLAB is set to nodefault in which case it is fine). # example: # DISTFILES=package-1.0.tgz # should be # DISTNAME= package-1.0 # EXTRACT_SUFX= .tgz if ($makevar{USE_GITHUB} ne 'nodefault' && $makevar{USE_GITLAB} ne 'nodefault') { if ($distfiles =~ /^\S+$/ && $distfiles !~ /:[^\/:]+$/) { $bogusdistfiles++; print "OK: seen DISTFILES with single item, checking value.\n" if ($verbose); &perror("WARN", $file, -1, "use of DISTFILES with single file ". "discouraged. distribution filename should be set by ". "DISTNAME and EXTRACT_SUFX."); if ($distfiles eq (($distname ne '') ? $distname : "$portname-$portversion") . $extractsufx) { &perror("WARN", $file, -1, "definition of DISTFILES not necessary. ". "DISTFILES is \${DISTNAME}/\${EXTRACT_SUFX} ". "by default."); } # display advice only in certain cases. #MICHAEL: will this work with multiple distfiles in this list? what about # doing the same sort of thing for DISTNAME, is it needed? if ($distfiles =~ /^\Q$i\E([\-.].+)$/) { &perror("WARN", $file, -1, "how about \"EXTRACT_SUFX=$1\"". ", instead of DISTFILES?"); } } } push(@varnames, qw( PORTNAME PORTVERSION DISTVERSIONPREFIX DISTVERSION DISTVERSIONSUFFIX PORTREVISION PORTEPOCH CATEGORIES MASTER_SITES MASTER_SITE_SUBDIR PROJECTHOST PKGNAMEPREFIX PKGNAMESUFFIX DISTNAME EXTRACT_SUFX DISTFILES DIST_SUBDIR EXTRACT_ONLY )); # # section 3: PATCH_SITES/PATCHFILES(optional) # print "OK: checking second section of $file (PATCH*: optional).\n" if ($verbose); $tmp = $sections[$idx] // ''; if ($tmp =~ /(PATCH_SITES|PATCH_SITE_SUBDIR|PATCHFILES|PATCH_DIST_STRIP)/) { &checkearlier($file, $tmp, @varnames); if ($tmp =~ /PATCH_SITES[?+]?=[^\n]+\n/) { print "OK: seen PATCH_SITES.\n" if ($verbose); $tmp =~ s/PATCH_SITES[?+]?=[^\n]+\n//; } if ($tmp =~ /PATCH_SITE_SUBDIR[?+]?=[^\n]+\n/) { print "OK: seen PATCH_SITE_SUBDIR.\n" if ($verbose); $tmp =~ s/PATCH_SITE_SUBDIR[?+]?=[^\n]+\n//; } if ($tmp =~ /PATCHFILES[?+]?=[^\n]+\n/) { print "OK: seen PATCHFILES.\n" if ($verbose); $tmp =~ s/PATCHFILES[?+]?=[^\n]+\n//; } if ($tmp =~ /PATCH_DIST_STRIP[?+]?=[^\n]+\n/) { print "OK: seen PATCH_DIST_STRIP.\n" if ($verbose); $tmp =~ s/PATCH_DIST_STRIP[?+]?=[^\n]+\n//; } &checkextra($tmp, 'PATCH_SITES', $file); $idx++; } push(@varnames, qw( PATCH_SITES PATCHFILES PATCH_DIST_STRIP )); # # section 4: MAINTAINER # print "OK: checking third section of $file (MAINTAINER).\n" if ($verbose); $tmp = $sections[$idx] // ''; &checkearlier($file, $tmp, @varnames); &checkorder('MAINTAINER', $tmp, $file, qw( MAINTAINER COMMENT )); $tmp = "\n" . $tmp; if ($tmp =~ /\nMAINTAINER(\?)?=([^\n]+)/) { my $addr = $2; if (defined $1 && $1 ne '') { &perror("WARN", $file, -1, "unless this is a master port, ". "MAINTAINER has to be set by \"=\", ". "not by \"$1=\".") unless ($masterport); } $addr =~ s/^\s*//; $addr =~ s/\s*$//; if ($addr =~ /[\s,<>()]/) { &perror("FATAL", $file, -1, "MAINTAINER should be a single address without comment."); } if ($addr !~ /^[^\@]+\@[\w\d\-\.]+$/) { &perror("FATAL", $file, -1, "MAINTAINER address, $addr, does not appear to be a valid email address."); } if ($newport && $addr =~ /ports\@freebsd.org/i) { &perror("WARN", $file, -1, "new ports should not be maintained by ". "ports\@FreeBSD.org."); } $tmp =~ s/\nMAINTAINER\??=[^\n]+//; } elsif ($whole !~ /\nMAINTAINER[?]?=/) { &perror("FATAL", $file, -1, "no MAINTAINER listed.") unless ($makevar{MAINTAINER} ne ''); if (!$slaveport && $makevar{MAINTAINER} ne '') { &perror("WARN", $file, -1, "MAINTAINER is set externally to this port's Makefile, but this port is not configured as a slave port."); } } $tmp =~ s/\n\n+/\n/g; # check COMMENT if ($tmp !~ /\nCOMMENT(.)?=/) { &perror("FATAL", $file, -1, "COMMENT has to be there.") unless ($makevar{COMMENT} ne ''); if (!$slaveport && $makevar{COMMENT} ne '') { &perror("WARN", $file, -1, "COMMENT is set externally to this port's Makefile, but this port is not configured as a slave port."); } } elsif (defined $1 && $1 ne '') { &perror("WARN", $file, -1, "unless this is a master port, COMMENT has to be set by \"=\", ". "not by \"$1=\".") unless ($masterport); } else { # check for correctness if (($makevar{COMMENT} !~ /^["\[0-9A-Z]/) || ($makevar{COMMENT} =~ m/\.$/)) { #" &perror("WARN", $file, -1, "COMMENT should begin with a capital, and end without a period"); } if (length($makevar{COMMENT}) > 70) { &perror("WARN", $file, -1, "COMMENT exceeds 70 characters limit."); } } $idx++; push(@varnames, qw( MAINTAINER COMMENT )); # # section 5: LICENSE # print "OK: checking fourth section of $file (LICENSE).\n" if ($verbose); $tmp = $sections[$idx] // ''; if ($makevar{LICENSE}) { &checkorder('LICENSE', $tmp, $file, qw( LICENSE LICENSE_COMB LICENSE_GROUPS(_\w+)? LICENSE_NAME(_\w+)? LICENSE_TEXT(_\w+)? LICENSE_FILE(_\w+)? LICENSE_PERMS(_\w+)? LICENSE_DISTFILES(_\w+)? )); # check LICENSE if ($makevar{LICENSE} && $makevar{LICENSE} ne '') { my $comb = $makevar{LICENSE_COMB} // 'single'; my @tokens = split(/ /, $makevar{LICENSE}); if ($comb eq 'single' && scalar(@tokens) > 1) { &perror("FATAL", $file, -1, "LICENSE contains multiple licenses but LICENSE_COMB is not set to 'dual' or 'multi'"); } } # check value of LICENSE_COMB if ($makevar{LICENSE_COMB} && $makevar{LICENSE_COMB} !~ /^(single|dual|multi$)/) { &perror("FATAL", $file, -1, "LICENSE_COMB contains invalid value '$1' - must be one of 'single', 'dual', 'multi'"); } # Check for proper license file usage while ($tmp =~ /\nLICENSE_FILE_([^\s=]+)([\s=])/g) { my $lfn = $1; my $nchar = $2; if (!grep(/\b$lfn\b/, $makevar{LICENSE})) { &perror("FATAL", $file, -1, "license specified is $makevar{LICENSE}, ". "but found LICENSE_FILE for $lfn."); } if ($lfn =~ /\+$/ && $nchar eq '=') { &perror("WARN", $file, -1, "if license ends with a '+', make sure ". "LICENSE_FILE_$lfn is followed by a space before the '='."); } } # Last-ditch check to make sure the license is sanely defined. my $lic_check = system("make check-license 2>&1 >/dev/null"); if ($lic_check) { &perror("FATAL", $file, -1, "Failed to validate port LICENSE '$makevar{LICENSE}' with ``make check-license''"); } $idx++; push(@varnames, qw( LICENSE LICENSE_COMB LICENSE_GROUPS LICENSE_NAME LICENSE_TEXT LICENSE_FILE LICENSE_PERMS )); } else { &perror("WARN", $file, -1, "Consider defining LICENSE."); } # # section 6: BROKEN/IGNORE/DEPRECATED (may not be there) # print "OK: checking sixth section of $file (BROKEN/IGNORE/DEPRECATED).\n" if ($verbose); $tmp = $sections[$idx] // ''; @linestocheck = qw( DEPRECATED EXPIRATION_DATE FORBIDDEN BROKEN(_\w+)? IGNORE(_\w+)? ONLY_FOR_ARCHS ONLY_FOR_ARCHS_REASON(_\w+)? NOT_FOR_ARCHS NOT_FOR_ARCHS_REASON(_\w+)? ); my $brokenpattern = "^(" . join("|", @linestocheck) . ")[?+:]?="; if ($tmp =~ /$brokenpattern/) { $idx++; } foreach my $i (@linestocheck) { $tmp =~ s/$i[?+:]?=[^\n]+\n//g; } push(@varnames, @linestocheck); &checkearlier($file, $tmp, @varnames); # # section 7: *_DEPENDS (may not be there) # print "OK: checking seventh section of $file (*_DEPENDS).\n" if ($verbose); $tmp = $sections[$idx] // ''; # Check for direct assignment of BUILD_DEPENDS to RUN_DEPENDS. if ($tmp =~ /\nRUN_DEPENDS=[ \t]*\$\{BUILD_DEPENDS}/) { &perror("FATAL", $file, -1, "RUN_DEPENDS should not be set to ". "\${BUILD_DEPENDS} as \${BUILD_DEPENDS} includes other ". "implicit dependencies. Instead, copy the explicit dependencies ". "from BUILD_DEPENDS to RUN_DEPENDS. See ". "http://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/makefile-depend.html#AEN2154 ". "for more details."); } @linestocheck = qw( EXTRACT_DEPENDS LIB_DEPENDS PATCH_DEPENDS BUILD_DEPENDS RUN_DEPENDS TEST_DEPENDS FETCH_DEPENDS DEPENDS_TARGET ); if ($tmp =~ /^([\w\d]+_)?(PATCH_|EXTRACT_|LIB_|BUILD_|RUN_|TEST_|FETCH_)DEPENDS/m) { &checkearlier($file, $tmp, @varnames); check_depends_syntax($tmp, $file); foreach my $i (@linestocheck) { $tmp =~ s/^([\w\d]+_)?$i[?+:]?=[^\n]+\n//g; } # Remove any other *_DEPENDS lines as people may # use a macro for common depends as described in # section 5.9.2 of the Porter's Handbook. $tmp =~ s/.+_DEPENDS[?+:]?=[^\n]+\n//g; &checkextra($tmp, '*_DEPENDS', $file); $idx++; } push(@varnames, @linestocheck); &checkearlier($file, $tmp, @varnames); # section 8: FLAVORS/FLAVOR(optional) # print "OK: check eighth section of $file (FLAVORS: optional).\n" if ($verbose); $tmp = $sections[$idx] // ''; if ($tmp =~ /(FLAVORS|FLAVOR)/) { &checkearlier($file, $tmp, @varnames); $idx++; } push(@varnames, qw( FLAVORS FLAVOR )); # section 9: USES/USE_x(optional) # print "OK: check ninth section of $file (USES: optional).\n" if ($verbose); $tmp = $sections[$idx] // ''; my $use_github_set = 0; if ($tmp =~ /(USES|USE_)/) { &checkearlier($file, $tmp, @varnames); foreach my $line (split(/(USE(?:S[?+]|[_\w\d]+)?=[^\n]+\n)/, $tmp)) { if ($line =~ /USES[?+]?=[^\n]+\n/) { print "OK: seen USES.\n" if ($verbose); $tmp =~ s/USES[?+]?=[^\n]+\n//; next; } if ($line =~ /USE([_\w\d]+)=[^\n]+\n/) { print "OK: seen USE$1.\n" if ($verbose); if ($tmp =~ /USE_GITHUB/) { $use_github_set = 1; } print "OK: USE_GITHUB set\n" if($use_github_set && $verbose); $tmp =~ s/USE([_\w\d]+)=[^\n]+\n//; next; } } print "OK: check if GH_ options are in use\n" if ($verbose); foreach my $line (split(/(GH(?:S[?+]|[_\w\d]+)?=[^\n]+\n)/, $tmp)) { if ($line =~ /GH([_\w\d]+)=[^\n]+\n/) { print "OK: seen GH$1.\n" if ($verbose); print "No USE_GITHUB seen but GH$1 used\n" unless ($use_github_set); $tmp =~ s/GH([_\w\d]+)=[^\n]+\n//; next; } } # XXX: We should check this. But, one is allowed to add _related_ items to # a USE_ or USES item in this same section. Since this would be an ever- # moving target, remove the check. #&checkextra($tmp, 'USES/USE_x', $file); $idx++; } push(@varnames, qw( USES )); # # Makefile 10: check the rest of file # print "OK: checking the rest of the $file.\n" if ($verbose); $tmp = join("\n\n", @sections[$idx .. scalar(@sections)-1]); $tmp = "\n" . $tmp; # to make the begin-of-line check easier &checkearlier($file, $tmp, @varnames); # Check depends that might be specified based on the WITH_/WITHOUT_ # arguments and other external variables. check_depends_syntax($tmp, $file); # check WRKSRC/NO_WRKSUBDIR # # do not use DISTFILES/DISTNAME to control over WRKSRC. # DISTNAME is for controlling distribution filename. # example: # DISTNAME= package # DISTFILES=package-1.0.tgz # should be # DISTNAME= package-1.0 # EXTRACT_SUFX=.tgz # WRKSRC= ${WRKDIR}/package # print "OK: checking WRKSRC.\n" if ($verbose); $wrksrc = $nowrksubdir = ''; $wrksrc = $1 if ($tmp =~ /\nWRKSRC[+?]?=[ \t]*([^\n]*)\n/); $nowrksubdir = $1 if ($tmp =~ /\nNO_WRKSUBDIR[+?]?=[ \t]*([^\n]*)\n/); if ($nowrksubdir eq '') { $realwrksrc = $wrksrc ? "$wrksrc/$distname" : "\${WRKDIR}/$distname"; } else { $realwrksrc = $wrksrc ? $wrksrc : '${WRKDIR}'; } print "OK: WRKSRC seems to be $realwrksrc.\n" if ($verbose); if ($nowrksubdir eq '') { print "OK: no NO_WRKSUBDIR, checking value of WRKSRC.\n" if ($verbose); if ($wrksrc eq 'work' || $wrksrc =~ /^$[\{\(]WRKDIR[\}\)]/) { &perror("WARN", $file, -1, "WRKSRC is set to meaningless value ". "\"$1\".". ($nowrksubdir eq '' ? " use \"NO_WRKSUBDIR=yes\" instead." : "")); } if ($bogusdistfiles) { if ($distname ne '' && $wrksrc eq '') { &perror("WARN", $file, -1, "do not use DISTFILES and DISTNAME ". "to control WRKSRC. how about ". "\"WRKSRC=\${WRKDIR}/$distname\"?"); } else { &perror("WARN", $file, -1, "DISTFILES/DISTNAME affects WRKSRC. ". "take caution when changing them."); } } } else { print "OK: seen NO_WRKSUBDIR, checking value of WRKSRC.\n" if ($verbose); if ($wrksrc eq 'work' || $wrksrc =~ /^$[\{\(]WRKDIR[\}\)]/) { &perror("WARN", $file, -1, "definition of WRKSRC not necessary. ". "WRKSRC is \${WRKDIR} by default."); } } - # check RESTRICTED/NO_CDROM/NO_PACKAGE - print "OK: checking RESTRICTED/NO_CDROM/NO_PACKAGE.\n" if ($verbose); - my $lps = $makevar{LICENSE_PERMS} // ''; - if ($committer && ($tmp =~ /\n(RESTRICTED|NO_CDROM|NO_PACKAGE)[+?]?=/ || - $lps =~ /\bno-\b/)) { - &perror("WARN", $file, -1, "Possible restrictive licensing found. ". - "If there are, in fact, limitations to use or distribution, please update ports/LEGAL."); - } - if ($tmp =~ /\nNO_PACKAGE[+?]?=/) { &perror("WARN", $file, -1, "NO_PACKAGE is obsolete. It should be ". "replaced with \"LICENSE_PERMS=no-pkg-mirror\""); } # check NO_STAGE if ($makevar{NO_STAGE}) { &perror("FATAL", $file, -1, "STAGE support is missing."); } # various MAN'uals related checks if ($makevar{USE_PERL5} =~ /\b(configure|modbuild|modbuildtiny)\b/ && $tmp =~ /\nMAN3PREFIX=\s*\$\{PREFIX}\/lib\/perl5\/\$\{PERL_VER/) { &perror("WARN", $file, -1, "MAN3PREFIX is ". "\"\${PREFIX}/lib/perl5/\${PERL_VERSION}\" ". "when USE_PERL5=configure|modbuild|modbuildtiny is set. You do not need to specify it."); } if ($tmp =~ /\nMAN[1-9LN][?]?=/) { &perror("FATAL", $file, -1, "MAN[1-9LN] macro is not supported anymore. ". "Please list manpages in plist."); } # check INFO print "OK: checking INFO.\n" if ($verbose); if ($tmp =~ /\nINFO=\s*([^\n]*)\n/) { my @minfo = grep($_ !~ /^\s*$/, split(/\s+/, $1)); if ($tmp =~ /[\/|\s]install-info\s/) { &perror("FATAL", $file, -1, "install-info is automatically run ". "when INFO is defined."); } foreach $i (@minfo) { if ($i =~ /\.info(-\d+)?$/) { &perror("FATAL", $file, -1, "do not include the .info extension ". "on files listed in the INFO macro."); } } } elsif ($tmp =~ /[\/|\s]install-info\s/) { &perror("WARN", $file, -1, "do not call install-info directly. Use the ". "INFO macro instead."); } # check for HAS_CONFIGURE or GNU_CONFIGURE if ($tmp =~ /\nGNU_CONFIGURE[?+]?=/ && $tmp =~ /\n(HAS_CONFIGURE)[?+]?=/) { &perror("WARN", $file, -1, "since you already have GNU_CONFIGURE, ". "you do not need $1."); } # check direct use of important make targets. if ($tmp =~ /\n(fetch|extract|patch|configure|build|install):/) { &perror("FATAL", $file, -1, "direct redefinition of make target \"$1\" ". "discouraged. redefine \"do-$1\" instead."); } # check for incorrect use of the pre-everything target. if ($tmp =~ /\npre-everything:[^:]/) { &perror("FATAL", $file, -1, "use pre-everything:: instead of pre-everything:"); } if ($tmp =~ /^pre-patch:/m && $use_gnome_hack) { &perror("FATAL", $file, -1, "pre-patch target overwrites gnomehack component. ". "use post-patch instead."); } if ($tmp =~ /^do-build:/m && $use_ant) { &perror("WARN", $file, -1, "USE_ANT is intended only for ports that ". "build with Ant. It is recommended not to override the default ". "'do-build:' target when defining USE_ANT"); } # # check for deprecated use of USE_RC_SUBR, and current syntax # if ($tmp =~ /\nUSE_RC_SUBR=([\s]*)(.*)/) { my $subr_value = $makevar{USE_RC_SUBR}; if ($subr_value eq '') { $subr_value = $2; } if (($subr_value =~ /^yes$/i) || ($subr_value =~ /^true$/i) || ($subr_value =~ /^1$/)) { &perror("FATAL", $file, -1, "The value of the USE_RC_SUBR variable ". "should be the name of the intended rc.d script, and there ". "should be a corresponding file in the files/ directory."); } else { foreach my $i (split(/\s/, $subr_value)) { if ($i ne '' && -f "files/$i.in") { if (open(RCIN, "< files/$i.in")) { my @rccontents = ; my $found_provide = 0; foreach my $line (@rccontents) { if ($line =~ /^# PROVIDE:/) { $found_provide = 1; last; } } if (!$found_provide) { &perror("FATAL", "files/$i.in", -1, "rc.d script ". "$i.in must contain a '# PROVIDE:' line in ". "order to be started at boot time."); } close(RCIN); } } } } } # check for health of SUB_FILES if ($tmp =~ /\nSUB_FILES=([\s]*)(.*)/) { my $subr_value = $makevar{SUB_FILES}; if ($subr_value eq '') { $subr_value = $2; } foreach my $i (split(/\s/, $subr_value)) { my $mvar; if ($i =~ /\$\{([^}]+)\}/) { $mvar = $1; if (defined($makevar{$mvar})) { $i = $makevar{$mvar}; } else { $i = &get_makevar($mvar); } } if ($i ne '' && ! -f "files/$i.in") { &perror("FATAL", $file, -1, "$i listed in SUB_FILES/USE_RC_SUBR, ". "but files/$i.in is missing."); } elsif ($i eq '' && $mvar && $mvar ne '') { &perror("WARN", $file, -1, "possible undefined make variable ". "$mvar used as the value for SUB_FILES/USE_RC_SUBR."); } } } 1; } sub perror($$$$) { my($type, $file, $line, $msg) = @_; if ($type eq 'FATAL') { $err++; } else { $warn++; } if ($grouperrs) { $msg = '%%LINES%%' . $msg; if ($file ne "") { $msg = $file . ": " . $msg; } $msg = $type . ": " . $msg; if (!$errcache{$msg}) { push @errlst, $msg; } if ($line > -1) { push @{$errcache{$msg}}, $line; } } else { if ($line > -1) { $msg = "[$line]: " . $msg; } if ($file ne "") { $msg = $file . ": " . $msg; } $msg = $type . ": " . $msg; print $msg . "\n"; } } sub checkextra { my($str, $section, $file) = @_; $str = "\n" . $str if ($str !~ /^\n/); $str =~ s/\n#[^\n]*/\n/g; $str =~ s/\n\.[^\n]+/\n/g; $str =~ s/\n\n+/\n/g; $str =~ s/^\s+//; $str =~ s/\s+$//; return if ($str eq ''); if ($str =~ /^([\w\d]+)/) { &perror("WARN", $file, -1, "extra item placed in the ". "$section section, ". "for example, \"$1\"."); } else { &perror("WARN", $file, -1, "extra item placed in the ". "$section section."); } } sub checkorder { my($section, $str, $file, @order) = @_; my(@items, $i, $j, $k, $invalidorder); print "OK: checking the order of $section section.\n" if ($verbose); $str //= ''; @items = (); foreach my $i (split("\n", $str)) { $i =~ s/[+?]?=.*$//; push(@items, $i); } @items = reverse(@items); $j = -1; $invalidorder = 0; while (scalar(@items)) { $i = pop(@items); $k = 0; while ($k < scalar(@order) && $order[$k] ne $i) { if (defined $order[$k] && $order[$k] =~ /[\.\*\+\?\{\}\[\]\^\$\|]/ && $i =~ /$order[$k]/) { last; } $k++; } if (defined $order[$k] && $order[$k] =~ /[\.\*\+\?\{\}\[\]\^\$\|]/ && $i =~ /$order[$k]/) { if ($k < $j) { &perror("FATAL", $file, -1, "$i appears out-of-order."); $invalidorder++; } else { print "OK: seen $i, in order.\n" if ($verbose); } $j = $k; } elsif (defined $order[$k] && $order[$k] eq $i) { if ($k < $j) { &perror("FATAL", $file, -1, "$i appears out-of-order."); $invalidorder++; } else { print "OK: seen $i, in order.\n" if ($verbose); } $j = $k; # This if condition tests for .if, .else (in all forms), # .for and .endfor and .include } elsif ($i !~ m/^\.(if|el|endif|for|endfor|include)/) { &perror("FATAL", $file, -1, "extra item \"$i\" placed in the ". "$section section."); } } if ($invalidorder) { &perror("FATAL", $file, -1, "order must be " . join('/', @order) . '.'); } else { print "OK: $section section is ordered properly.\n" if ($verbose); } } sub checkearlier { my($file, $str, @varnames) = @_; $str //= ''; print "OK: checking items that have to appear earlier.\n" if ($verbose); foreach my $i (@varnames) { if ($str =~ /\n($i)\??=/) { &perror("WARN", $file, -1, "\"$1\" has to appear earlier."); } } } sub linenumber { my $text = shift; my @lines; @lines = split /\n/, $text; return scalar(@lines) - 1; } sub abspathname { my($str, $file) = @_; my($s, $i, %cmdnames); my($pre); # trim all trailing backslash and newline $str =~ s/\\\n\s*/ /g; # ignore parameter string to reinplace command $str =~ s/([ \t][\@-]?(?:sed|\$[\{\(]SED[\}\)]|\$[\{\(]REINPLACE_CMD[\}\)]))((?:\s+\-\w+)*\s+(?:"(?:\\"|[^"\n])*"|'(?:\\'|[^'\n])*'))+(.*)/$1$3/g; #' # ignore parameter string to echo command # XXX: This next pattern crashes Perl 5.8.7. #$str =~ s/[ \t][\@-]?(echo|\$[\{\(]ECHO[\}\)]|\$[\{\(]ECHO_MSG[\}\)])[ \t]+("(\\'|\\"|[^"])*"|'(\\'|\\"|[^"])*')[ \t]*[;\n]//; #' $str =~ s/[ \t][\@-]?(echo|\$[\{\(]ECHO[\}\)]|\$[\{\(]ECHO_MSG[\}\)])[ \t]+.*(;|$)//m; #' print "OK: checking direct use of full pathnames in $file.\n" if ($verbose); foreach my $s (split(/\n+/, $str)) { $i = ''; $s =~ s/#.*$//; if ($s =~ /(^|[ \t\@'"-])(\/[\w\d])/) { #' # suspected pathnames are recorded. $i = $2 . $'; $pre = $` . $1; if ($pre =~ /MASTER_SITE_SUBDIR/) { # MASTER_SITE_SUBDIR lines are ok. $i = ''; } if ($s =~ /\$\{[^}]*?\Q$i\E/) { # If we're inside a make variable, we probably do not have # an absolute path. $i = ''; } } if ($i ne '' && ! grep {$i =~ m|^$_|} @ALLOWED_FULL_PATHS) { $i =~ s/\s.*$//; $i =~ s/['"].*$//; #' $i = substr($i, 0, 20) . '...' if (20 < length($i)); &perror("WARN", $file, -1, "possible use of absolute pathname ". "\"$i\"."); } } foreach my $s (split(/\n+/, $str)) { print "OK: checking direct use of pathnames, phase 1.\n" if ($verbose); %cmdnames = split(/\n|\t+/, <