File: [cvs.NetBSD.org] / pkgsrc / pkgtools / pkglint / files / Attic / vartypecheck_test.go (download)
Revision 1.51, Sun Jun 30 20:56:19 2019 UTC (4 years, 8 months ago) by rillig
Branch: MAIN
Changes since 1.50: +63 -14
lines
pkgtools/pkglint: update to 5.7.14
Changes since 5.7.13:
- Removed the -Cextra command line option since it didn't produce useful
warnings.
- Removed unwarranted warnings about _WRAP_EXTRA_ARGS.CC being used in
packages.
- Cleaned up the canonical order of variables in package Makefiles.
- Added a few commands to those that cannot fail, to reduce the number of
"at the left of the | operator" in shell programs.
- Fixed warnings about "-ggdb" being an unknown shell command.
- Reduced number of warnings about lists being used where a single value
is expected.
- Replaced unreliable check for invalid CFLAGS and LDFLAGS with a more
practical check.
- Renamed "RCS tag" to "CVS tag" to make the diagnostics more modern.
- Added warning when PKGNAME or PKGVERSION is used in MASTER_SITES.
- Reworded warning for missing or superfluous PLIST files.
- Lots of other detail changes, refactorings and automatic tests.
|
package pkglint
import (
"gopkg.in/check.v1"
)
func (s *Suite) Test_VartypeCheck_AwkCommand(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).AwkCommand)
vt.Varname("PLIST_AWK")
vt.Op(opAssignAppend)
vt.Values(
"{print $0}",
"{print $$0}")
t.DisableTracing()
vt.Values(
"{print $0}",
"{print $$0}")
// TODO: In this particular context of AWK programs, $$0 is not a shell variable.
// The warning should be adjusted to reflect this.
vt.Output(
"WARN: filename.mk:1: $0 is ambiguous. "+
"Use ${0} if you mean a Make variable or $$0 if you mean a shell variable.",
"WARN: filename.mk:3: $0 is ambiguous. "+
"Use ${0} if you mean a Make variable or $$0 if you mean a shell variable.")
}
func (s *Suite) Test_VartypeCheck_BasicRegularExpression(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).BasicRegularExpression)
vt.Varname("REPLACE_FILES.pl")
vt.Values(
".*\\.pl$",
".*\\.pl$$")
t.DisableTracing()
vt.Values(
".*\\.pl$",
".*\\.pl$$")
vt.Output(
"WARN: filename.mk:1: Internal pkglint error in MkLine.Tokenize at \"$\".",
"WARN: filename.mk:3: Internal pkglint error in MkLine.Tokenize at \"$\".")
}
func (s *Suite) Test_VartypeCheck_BuildlinkDepmethod(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).BuildlinkDepmethod)
vt.Varname("BUILDLINK_DEPMETHOD.libc")
vt.Op(opAssignDefault)
vt.Values(
"full",
"unknown",
"${BUILDLINK_DEPMETHOD.kernel}")
vt.Output(
"WARN: filename.mk:2: Invalid dependency method \"unknown\". Valid methods are \"build\" or \"full\".")
}
func (s *Suite) Test_VartypeCheck_Category(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).Category)
t.CreateFileLines("filesyscategory/Makefile",
"# empty")
t.CreateFileLines("wip/Makefile",
"# empty")
vt.Varname("CATEGORIES")
vt.Values(
"chinese",
"arabic",
"filesyscategory",
"wip")
vt.Output(
"ERROR: filename.mk:2: Invalid category \"arabic\".",
"ERROR: filename.mk:4: Invalid category \"wip\".")
}
func (s *Suite) Test_VartypeCheck_CFlag(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).CFlag)
vt.tester.SetUpTool("pkg-config", "", AtRunTime)
vt.Varname("CFLAGS")
vt.Op(opAssignAppend)
vt.Values(
"-Wall",
"/W3",
"target:sparc64",
"-std=c99",
"-XX:+PrintClassHistogramAfterFullGC",
"`pkg-config pidgin --cflags`",
"-c99",
"-c",
"-no-integrated-as",
"-pthread",
"`pkg-config`_plus")
vt.OutputEmpty()
vt.Values(
"-L${PREFIX}/lib",
"-L${PREFIX}/lib64",
"-lncurses",
"-DMACRO=\\\"",
"-DMACRO=\\'")
vt.Output(
"WARN: filename.mk:21: \"-L${PREFIX}/lib\" is a linker flag "+
"and belong to LDFLAGS, LIBS or LDADD instead of CFLAGS.",
"WARN: filename.mk:22: \"-L${PREFIX}/lib64\" is a linker flag "+
"and belong to LDFLAGS, LIBS or LDADD instead of CFLAGS.",
"WARN: filename.mk:23: \"-lncurses\" is a linker flag "+
"and belong to LDFLAGS, LIBS or LDADD instead of CFLAGS.",
"WARN: filename.mk:24: Compiler flag \"-DMACRO=\\\\\\\"\" "+
"has unbalanced double quotes.",
"WARN: filename.mk:25: Compiler flag \"-DMACRO=\\\\'\" "+
"has unbalanced single quotes.")
vt.Op(opUseMatch)
vt.Values(
"anything")
vt.OutputEmpty()
}
func (s *Suite) Test_VartypeCheck_Comment(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).Comment)
G.Pkg = NewPackage(t.File("category/converter"))
G.Pkg.EffectivePkgbase = "converter"
vt.Varname("COMMENT")
vt.Values(
"Versatile Programming Language",
"TODO: Short description of the package",
"A great package.",
"some packages need a very very long comment to explain their basic usefulness",
"\"Quoting the comment is wrong\"",
"'Quoting the comment is wrong'",
"Package is a great package",
"Package is an awesome package",
"The Big New Package is a great package",
"Converter converts between measurement units",
"Converter is a unit converter",
"\"Official\" office suite",
"'SQL injection fuzzer")
vt.Output(
"ERROR: filename.mk:2: COMMENT must be set.",
"WARN: filename.mk:3: COMMENT should not begin with \"A\".",
"WARN: filename.mk:3: COMMENT should not end with a period.",
"WARN: filename.mk:4: COMMENT should start with a capital letter.",
"WARN: filename.mk:4: COMMENT should not be longer than 70 characters.",
"WARN: filename.mk:5: COMMENT should not be enclosed in quotes.",
"WARN: filename.mk:6: COMMENT should not be enclosed in quotes.",
"WARN: filename.mk:7: COMMENT should not contain \"is a\".",
"WARN: filename.mk:8: COMMENT should not contain \"is an\".",
"WARN: filename.mk:9: COMMENT should not contain \"is a\".",
"WARN: filename.mk:10: COMMENT should not start with the package name.",
"WARN: filename.mk:11: COMMENT should not start with the package name.",
"WARN: filename.mk:11: COMMENT should not contain \"is a\".")
}
func (s *Suite) Test_VartypeCheck_ConfFiles(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).ConfFiles)
vt.Varname("CONF_FILES")
vt.Op(opAssignAppend)
vt.Values(
"single/file",
"share/etc/config ${PKG_SYSCONFDIR}/etc/config",
"share/etc/config ${PKG_SYSCONFBASE}/etc/config file",
"share/etc/config ${PREFIX}/etc/config share/etc/config2 ${VARBASE}/config2",
"share/etc/bootrc /etc/bootrc")
vt.Output(
"WARN: filename.mk:1: Values for CONF_FILES should always be pairs of paths.",
"WARN: filename.mk:3: Values for CONF_FILES should always be pairs of paths.",
"WARN: filename.mk:5: The destination file \"/etc/bootrc\" should start with a variable reference.")
}
// See Test_MkParser_Dependency.
func (s *Suite) Test_VartypeCheck_Dependency(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Dependency)
vt.Varname("CONFLICTS")
vt.Op(opAssignAppend)
// comparison operators
vt.Values(
"perl5>=5.22",
"libkipi>=0.1.5<4.0",
"gtk2+>=2.16")
vt.OutputEmpty()
// pattern matching
vt.Values(
"perl-5*",
"perl5-*",
"perl-5.22",
"perl5-5.22.*",
"gtksourceview-sharp-2.0-[0-9]*")
vt.Output(
"WARN: filename.mk:11: Please use \"5.*\" instead of \"5*\" as the version pattern.",
"WARN: filename.mk:12: Please use \"perl5-[0-9]*\" instead of \"perl5-*\".",
"WARN: filename.mk:13: Please use \"5.22{,nb*}\" instead of \"5.22\" as the version pattern.",
"WARN: filename.mk:15: The version pattern \"2.0-[0-9]*\" should not contain a hyphen.")
// nb suffix
vt.Values(
"perl5-5.22.*{,nb*}",
"perl-5.22{,nb*}",
"perl-5.22{,nb[0-9]*}",
"mbrola-301h{,nb[0-9]*}",
"ncurses-${NC_VERS}{,nb*}",
"gnome-control-center>=2.20.1{,nb*}",
"gnome-control-center>=2.20.1{,nb[0-9]*}")
vt.Output(
"WARN: filename.mk:26: Dependency patterns of the form pkgbase>=1.0 don't need the \"{,nb*}\" extension.",
"WARN: filename.mk:27: Dependency patterns of the form pkgbase>=1.0 don't need the \"{,nb*}\" extension.")
// alternative patterns, using braces or brackets
vt.Values(
"mpg123{,-esound,-nas}>=0.59.18",
"mysql*-{client,server}-[0-9]*",
"{ssh{,6}-[0-9]*,openssh-[0-9]*}",
"libao-[a-z]*-[0-9]*")
vt.OutputEmpty()
// variables
vt.Values(
"postgresql8[0-35-9]-${module}-[0-9]*",
"${_EMACS_CONFLICTS.${_EMACS_FLAVOR}}",
"${PYPKGPREFIX}-sqlite3",
"${PYPKGPREFIX}-sqlite3-${VERSION}",
"${PYPKGPREFIX}-sqlite3-${PYSQLITE_REQD}",
"${PYPKGPREFIX}-sqlite3>=${PYSQLITE_REQD}",
"${EMACS_PACKAGE}>=${EMACS_MAJOR}",
// The "*" is ambiguous. It could either continue the PKGBASE or
// start the version number.
"${PKGNAME_NOREV:S/jdk/jre/}*",
// The canonical form is "{,nb*}" instead of "{nb*,}".
// Plus, mentioning nb* is not necessary when using >=.
"dovecot>=${PKGVERSION_NOREV}{nb*,}",
"oxygen-icons>=${KF5VER}{,nb[0-9]*}",
// The following pattern should have "]*}" instead of "]}*".
"ja-vflib-lib-${VFLIB_VERSION}{,nb[0-9]}*",
// The following pattern uses both ">=" and "*", which doesn't make sense.
"${PYPKGPREFIX}-sphinx>=1.2.3nb1*",
"{${NETSCAPE_PREFERRED:C/:/,/g}}-[0-9]*")
vt.Output(
"WARN: filename.mk:43: Invalid dependency pattern \"${PYPKGPREFIX}-sqlite3\".",
// This pattern is invalid because the variable name doesn't contain "VER".
"WARN: filename.mk:45: Invalid dependency pattern \"${PYPKGPREFIX}-sqlite3-${PYSQLITE_REQD}\".",
"WARN: filename.mk:48: Invalid dependency pattern \"${PKGNAME_NOREV:S/jdk/jre/}*\".",
"WARN: filename.mk:49: Invalid dependency pattern \"dovecot>=${PKGVERSION_NOREV}{nb*,}\".",
"WARN: filename.mk:50: Dependency patterns of the form pkgbase>=1.0 don't need the \"{,nb*}\" extension.",
"WARN: filename.mk:51: Invalid dependency pattern \"ja-vflib-lib-${VFLIB_VERSION}{,nb[0-9]}*\".",
"WARN: filename.mk:52: Invalid dependency pattern \"${PYPKGPREFIX}-sphinx>=1.2.3nb1*\".")
// invalid dependency patterns
vt.Values(
"Perl",
"py-docs",
"perl5-[5.10-5.22]*",
"package-1.0|garbage",
"package>=1.0:../../category/package",
"package-1.0>=1.0.3",
// This should be regarded as invalid since the [a-z0-9] might either
// continue the PKGBASE or start the version number.
"${RUBY_PKGPREFIX}-theme-[a-z0-9]*")
vt.Output(
"WARN: filename.mk:61: Invalid dependency pattern \"Perl\".",
"WARN: filename.mk:62: Invalid dependency pattern \"py-docs\".",
"WARN: filename.mk:63: Only [0-9]* is allowed in the numeric part of a dependency.",
"WARN: filename.mk:63: The version pattern \"[5.10-5.22]*\" should not contain a hyphen.",
"WARN: filename.mk:64: Invalid dependency pattern \"package-1.0|garbage\".",
// TODO: Mention that the path should be removed.
"WARN: filename.mk:65: Invalid dependency pattern \"package>=1.0:../../category/package\".",
// TODO: Mention that version numbers in a pkgbase must be appended directly, without hyphen.
"WARN: filename.mk:66: Invalid dependency pattern \"package-1.0>=1.0.3\".",
"WARN: filename.mk:67: Invalid dependency pattern \"${RUBY_PKGPREFIX}-theme-[a-z0-9]*\".")
}
func (s *Suite) Test_VartypeCheck_DependencyWithPath(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).DependencyWithPath)
t.CreateFileLines("category/package/Makefile")
t.CreateFileLines("databases/py-sqlite3/Makefile")
t.CreateFileLines("devel/gettext/Makefile")
t.CreateFileLines("devel/gmake/Makefile")
t.CreateFileLines("devel/py-module/Makefile")
t.CreateFileLines("x11/alacarte/Makefile")
G.Pkg = NewPackage(t.File("category/package"))
vt.Varname("DEPENDS")
vt.Op(opAssignAppend)
vt.File(G.Pkg.File("filename.mk"))
vt.Values(
"Perl",
"perl5>=5.22:../perl5",
"perl5>=5.24:../../lang/perl5",
"gtk2+>=2.16:../../x11/alacarte",
"gettext-[0-9]*:../../devel/gettext",
"gmake-[0-9]*:../../devel/gmake")
vt.Output(
"WARN: ~/category/package/filename.mk:1: Invalid dependency pattern with path \"Perl\".",
"WARN: ~/category/package/filename.mk:2: Dependency paths should have the form \"../../category/package\".",
"ERROR: ~/category/package/filename.mk:2: Relative path \"../perl5/Makefile\" does not exist.",
"WARN: ~/category/package/filename.mk:2: \"../perl5\" is not a valid relative package directory.",
"WARN: ~/category/package/filename.mk:2: Please use USE_TOOLS+=perl:run instead of this dependency.",
"ERROR: ~/category/package/filename.mk:3: Relative path \"../../lang/perl5/Makefile\" does not exist.",
"WARN: ~/category/package/filename.mk:3: Please use USE_TOOLS+=perl:run instead of this dependency.",
"WARN: ~/category/package/filename.mk:5: Please use USE_TOOLS+=msgfmt instead of this dependency.",
"WARN: ~/category/package/filename.mk:6: Please use USE_TOOLS+=gmake instead of this dependency.")
vt.Values(
"broken0.12.1:../../x11/alacarte", // missing version
"broken[0-9]*:../../x11/alacarte", // missing version
"broken[0-9]*../../x11/alacarte", // missing colon
"broken>=:../../x11/alacarte", // incomplete comparison
"broken=0:../../x11/alacarte", // invalid comparison operator
"broken=:../../x11/alacarte", // incomplete comparison
"broken-:../../x11/alacarte", // incomplete pattern
"broken>:../../x11/alacarte") // incomplete comparison
vt.Output(
"WARN: ~/category/package/filename.mk:11: Invalid dependency pattern \"broken0.12.1\".",
"WARN: ~/category/package/filename.mk:12: Invalid dependency pattern \"broken[0-9]*\".",
"WARN: ~/category/package/filename.mk:13: Invalid dependency pattern with path \"broken[0-9]*../../x11/alacarte\".",
"WARN: ~/category/package/filename.mk:14: Invalid dependency pattern \"broken>=\".",
"WARN: ~/category/package/filename.mk:15: Invalid dependency pattern \"broken=0\".",
"WARN: ~/category/package/filename.mk:16: Invalid dependency pattern \"broken=\".",
"WARN: ~/category/package/filename.mk:17: Invalid dependency pattern \"broken-\".",
"WARN: ~/category/package/filename.mk:18: Invalid dependency pattern \"broken>\".")
vt.Values(
"${PYPKGPREFIX}-sqlite3:../../${MY_PKGPATH.py-sqlite3}",
"${PYPKGPREFIX}-sqlite3:../../databases/py-sqlite3",
"${DEPENDS.NetBSD}",
"${DEPENDENCY_PATTERN.py-sqlite3}:${DEPENDENCY_PATH.py-sqlite}",
"${PYPKGPREFIX}-module>=0:../../devel/py-module",
"${EMACS_PACKAGE}>=${EMACS_MAJOR}:${EMACS_PKGDIR}",
"{${NETSCAPE_PREFERRED:C/:/,/g}}-[0-9]*:../../www/${NETSCAPE_PREFERRED:C/:.*//}")
vt.Output(
"WARN: ~/category/package/filename.mk:21: "+
"Invalid dependency pattern \"${PYPKGPREFIX}-sqlite3\".",
"WARN: ~/category/package/filename.mk:22: "+
"Invalid dependency pattern \"${PYPKGPREFIX}-sqlite3\".")
}
func (s *Suite) Test_VartypeCheck_DistSuffix(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).DistSuffix)
vt.Varname("EXTRACT_SUFX")
vt.Values(
".tar.gz",
".tar.bz2",
".tar.gz # overrides a definition from a Makefile.common")
vt.Output(
"NOTE: filename.mk:1: EXTRACT_SUFX is \".tar.gz\" by default, so this definition may be redundant.")
}
func (s *Suite) Test_VartypeCheck_EmulPlatform(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).EmulPlatform)
vt.Varname("EMUL_PLATFORM")
vt.Values(
"linux-i386",
"nextbsd-8087",
"${LINUX}")
vt.Output(
"WARN: filename.mk:2: \"nextbsd\" is not valid for the operating system part of EMUL_PLATFORM. "+
"Use one of "+
"{ bitrig bsdos cygwin darwin dragonfly freebsd haiku hpux "+
"interix irix linux mirbsd netbsd openbsd osf1 solaris sunos "+
"} instead.",
"WARN: filename.mk:2: \"8087\" is not valid for the hardware architecture part of EMUL_PLATFORM. "+
"Use one of { "+
"aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 "+
"cobalt coldfire convex dreamcast "+
"earm earmeb earmhf earmhfeb earmv4 earmv4eb earmv5 earmv5eb "+
"earmv6 earmv6eb earmv6hf earmv6hfeb "+
"earmv7 earmv7eb earmv7hf earmv7hfeb evbarm "+
"hpcmips hpcsh hppa hppa64 "+
"i386 i586 i686 ia64 m68000 m68k m88k "+
"mips mips64 mips64eb mips64el mipseb mipsel mipsn32 "+
"mlrisc ns32k pc532 pmax powerpc powerpc64 rs6000 "+
"s390 sh3eb sh3el sparc sparc64 vax x86_64 "+
"} instead.",
"WARN: filename.mk:3: \"${LINUX}\" is not a valid emulation platform.")
}
func (s *Suite) Test_VartypeCheck_Enum(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), enum("jdk1 jdk2 jdk4").checker)
vt.Varname("JDK")
vt.Op(opUseMatch)
vt.Values(
"*",
"jdk*",
"sun-jdk*",
"${JDKNAME}",
"[")
vt.Output(
"WARN: filename.mk:3: The pattern \"sun-jdk*\" cannot match any of { jdk1 jdk2 jdk4 } for JDK.",
"WARN: filename.mk:5: Invalid match pattern \"[\".")
}
func (s *Suite) Test_VartypeCheck_Enum__use_match(c *check.C) {
t := s.Init(c)
t.SetUpVartypes()
t.SetUpCommandLine("-Wall", "--explain")
mklines := t.NewMkLines("module.mk",
MkCvsID,
"",
".if !empty(MACHINE_ARCH:Mi386) || ${MACHINE_ARCH} == i386",
".endif",
".if !empty(PKGSRC_COMPILER:Mclang) || ${PKGSRC_COMPILER} == clang",
".endif",
".if ${MACHINE_ARCH:Ni386:Nx86_64:Nsparc64}",
".endif")
mklines.Check()
t.CheckOutputLines(
"NOTE: module.mk:3: MACHINE_ARCH should be compared using == instead of matching against \":Mi386\".",
"",
"\tThis variable has a single value, not a list of values. Therefore it",
"\tfeels strange to apply list operators like :M and :N onto it. A more",
"\tdirect approach is to use the == and != operators.",
"",
"\tAn entirely different case is when the pattern contains wildcards",
"\tlike ^, *, $. In such a case, using the :M or :N modifiers is useful",
"\tand preferred.",
"",
"WARN: module.mk:5: Use ${PKGSRC_COMPILER:Mclang} instead of the == operator.",
"",
"\tThe PKGSRC_COMPILER can be a list of chained compilers, e.g. \"ccache",
"\tdistcc clang\". Therefore, comparing it using == or != leads to wrong",
"\tresults in these cases.",
"")
}
func (s *Suite) Test_VartypeCheck_FetchURL(c *check.C) {
t := s.Init(c)
t.SetUpPackage("category/own-master-site",
"MASTER_SITE_OWN=\thttps://example.org/")
t.FinishSetUp()
vt := NewVartypeCheckTester(t, (*VartypeCheck).FetchURL)
t.SetUpMasterSite("MASTER_SITE_GNU", "http://ftp.gnu.org/pub/gnu/")
t.SetUpMasterSite("MASTER_SITE_GITHUB", "https://github.com/")
G.Pkg = NewPackage(t.File("category/own-master-site"))
G.Pkg.load()
vt.Varname("MASTER_SITES")
vt.Values(
"https://github.com/example/project/",
"http://ftp.gnu.org/pub/gnu/bison", // Missing a slash at the end
"${MASTER_SITE_GNU:=bison}",
"${MASTER_SITE_INVALID:=subdir/}",
"${MASTER_SITE_OWN}",
"${MASTER_SITE_OWN:=subdir/}")
vt.Output(
"WARN: filename.mk:1: Please use ${MASTER_SITE_GITHUB:=example/} "+
"instead of \"https://github.com/example/project/\" "+
"and run \""+confMake+" help topic=github\" for further tips.",
"WARN: filename.mk:2: Please use ${MASTER_SITE_GNU:=bison} "+
"instead of \"http://ftp.gnu.org/pub/gnu/bison\".",
"ERROR: filename.mk:3: The subdirectory in MASTER_SITE_GNU must end with a slash.",
"ERROR: filename.mk:4: The site MASTER_SITE_INVALID does not exist.")
// PR 46570, keyword gimp-fix-ca
vt.Values(
"https://example.org/download.cgi?filename=filename&sha1=12341234")
vt.OutputEmpty()
vt.Values(
"http://example.org/distfiles/",
"http://example.org/download?filename=distfile;version=1.0",
"http://example.org/download?filename=<distfile>;version=<version>")
vt.Output(
"WARN: filename.mk:23: \"http://example.org/download?filename=<distfile>;version=<version>\" is not a valid URL.")
vt.Values(
"${MASTER_SITE_GITHUB:S,^,-,:=project/archive/${DISTFILE}}")
// No warning that the part after the := must end with a slash,
// since there is another modifier in the variable use, in this case :S.
//
// That modifier adds a hyphen at the beginning (but pkglint doesn't
// inspect this), therefore the URL is not required to end with a slash anymore.
vt.OutputEmpty()
// As of June 2019, the :S modifier is not analyzed since it is unusual.
vt.Values(
"${MASTER_SITE_GNU:S,$,subdir/,}")
vt.OutputEmpty()
}
func (s *Suite) Test_VartypeCheck_FetchURL__without_package(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).FetchURL)
vt.Varname("MASTER_SITES")
vt.Values(
"https://github.com/example/project/",
"${MASTER_SITE_OWN}")
vt.Output(
"ERROR: filename.mk:2: The site MASTER_SITE_OWN does not exist.")
}
func (s *Suite) Test_VartypeCheck_Filename(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Filename)
vt.Varname("FNAME")
vt.Values(
"Filename with spaces.docx",
"OS/2-manual.txt")
vt.Output(
"WARN: filename.mk:1: The filename \"Filename with spaces.docx\" contains the invalid characters \" \".",
"WARN: filename.mk:2: The filename \"OS/2-manual.txt\" contains the invalid character \"/\".")
vt.Op(opUseMatch)
vt.Values(
"Filename with spaces.docx")
// There's no guarantee that a filename only contains [A-Za-z0-9.].
// Therefore there are no useful checks in this situation.
vt.Output(
"WARN: filename.mk:11: The filename pattern \"Filename with spaces.docx\" " +
"contains the invalid characters \" \".")
}
func (s *Suite) Test_VartypeCheck_FileMask(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).FileMask)
vt.Varname("FNAME")
vt.Values(
"filename.txt",
"*.txt",
"[12345].txt",
"[0-9].txt",
"???.txt",
"FileMask with spaces.docx",
"OS/2-manual.txt")
vt.Output(
"WARN: filename.mk:6: The filename pattern \"FileMask with spaces.docx\" "+
"contains the invalid characters \" \".",
"WARN: filename.mk:7: The filename pattern \"OS/2-manual.txt\" "+
"contains the invalid character \"/\".")
vt.Op(opUseMatch)
vt.Values(
"FileMask with spaces.docx")
// There's no guarantee that a filename only contains [A-Za-z0-9.].
// Therefore it might be necessary to allow all characters here.
// TODO: Investigate whether this restriction is useful in practice.
vt.Output(
"WARN: filename.mk:11: The filename pattern \"FileMask with spaces.docx\" " +
"contains the invalid characters \" \".")
}
func (s *Suite) Test_VartypeCheck_FileMode(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).FileMode)
vt.Varname("HIGHSCORE_PERMS")
vt.Values(
"u+rwx",
"0600",
"1234",
"12345",
"${OTHER_PERMS}",
"")
vt.Output(
"WARN: filename.mk:1: Invalid file mode \"u+rwx\".",
"WARN: filename.mk:4: Invalid file mode \"12345\".",
"WARN: filename.mk:6: Invalid file mode \"\".")
vt.Op(opUseMatch)
vt.Values(
"u+rwx")
// There's no guarantee that a filename only contains [A-Za-z0-9.].
// Therefore there are no useful checks in this situation.
vt.Output(
"WARN: filename.mk:11: Invalid file mode \"u+rwx\".")
}
func (s *Suite) Test_VartypeCheck_GccReqd(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).GccReqd)
vt.Varname("GCC_REQD")
vt.Op(opAssignAppend)
vt.Values(
"2.95",
"3.1.5",
"4.7",
"4.8",
"5.1",
"6",
"7.3")
vt.Output(
"WARN: filename.mk:5: GCC version numbers should only contain the major version (5).",
"WARN: filename.mk:7: GCC version numbers should only contain the major version (7).")
}
func (s *Suite) Test_VartypeCheck_Homepage(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).Homepage)
vt.Varname("HOMEPAGE")
vt.Values(
"http://www.pkgsrc.org/",
"https://www.pkgsrc.org/",
"${MASTER_SITES}")
vt.Output(
"WARN: filename.mk:3: HOMEPAGE should not be defined in terms of MASTER_SITEs.")
G.Pkg = NewPackage(t.File("category/package"))
vt.Values(
"${MASTER_SITES}")
// When this assignment occurs while checking a package, but the package
// doesn't define MASTER_SITES, that variable cannot be expanded, which means
// the warning cannot refer to its value.
vt.Output(
"WARN: filename.mk:11: HOMEPAGE should not be defined in terms of MASTER_SITEs.")
delete(G.Pkg.vars.firstDef, "MASTER_SITES")
delete(G.Pkg.vars.lastDef, "MASTER_SITES")
G.Pkg.vars.Define("MASTER_SITES", t.NewMkLine(G.Pkg.File("Makefile"), 5,
"MASTER_SITES=\thttps://cdn.NetBSD.org/pub/pkgsrc/distfiles/"))
vt.Values(
"${MASTER_SITES}")
vt.Output(
"WARN: filename.mk:21: HOMEPAGE should not be defined in terms of MASTER_SITEs. " +
"Use https://cdn.NetBSD.org/pub/pkgsrc/distfiles/ directly.")
delete(G.Pkg.vars.firstDef, "MASTER_SITES")
delete(G.Pkg.vars.lastDef, "MASTER_SITES")
G.Pkg.vars.Define("MASTER_SITES", t.NewMkLine(G.Pkg.File("Makefile"), 5,
"MASTER_SITES=\t${MASTER_SITE_GITHUB}"))
vt.Values(
"${MASTER_SITES}")
// When MASTER_SITES itself makes use of another variable, pkglint doesn't
// resolve that variable and just outputs the simple variant of this warning.
vt.Output(
"WARN: filename.mk:31: HOMEPAGE should not be defined in terms of MASTER_SITEs.")
}
func (s *Suite) Test_VartypeCheck_Identifier(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).Identifier)
vt.Varname("MYSQL_CHARSET")
vt.Values(
"${OTHER_VAR}",
"identifiers cannot contain spaces",
"id/cannot/contain/slashes",
"id-${OTHER_VAR}",
"")
vt.Output(
"WARN: filename.mk:2: Invalid identifier \"identifiers cannot contain spaces\".",
"WARN: filename.mk:3: Invalid identifier \"id/cannot/contain/slashes\".",
"WARN: filename.mk:5: Invalid identifier \"\".")
vt.Op(opUseMatch)
vt.Values(
"[A-Z]",
"[A-Z.]",
"${PKG_OPTIONS:Moption}",
"A*B")
vt.Output(
"WARN: filename.mk:12: Invalid identifier pattern \"[A-Z.]\" for MYSQL_CHARSET.")
}
func (s *Suite) Test_VartypeCheck_Integer(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).Integer)
vt.Varname("NUMBER")
vt.Values(
"${OTHER_VAR}",
"123",
"-13",
"11111111111111111111111111111111111111111111111")
vt.Output(
"WARN: filename.mk:1: Invalid integer \"${OTHER_VAR}\".",
"WARN: filename.mk:3: Invalid integer \"-13\".")
}
func (s *Suite) Test_VartypeCheck_LdFlag(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).LdFlag)
vt.tester.SetUpTool("pkg-config", "", AtRunTime)
vt.Varname("LDFLAGS")
vt.Op(opAssignAppend)
vt.Values(
"-lc",
"-L/usr/lib64",
"`pkg-config pidgin --ldflags`",
"-unknown",
"no-hyphen",
"-Wl,--rpath,/usr/lib64",
"-pthread",
"-static",
"-static-something",
"${LDFLAGS.NetBSD}",
"-l${LIBNCURSES}",
"`pkg-config`_plus",
"-DMACRO",
"-UMACRO",
"-P",
"-E",
"-I${PREFIX}/include")
vt.Op(opUseMatch)
vt.Values(
"anything")
vt.Output(
"WARN: filename.mk:6: Please use \"${COMPILER_RPATH_FLAG}\" instead of \"-Wl,--rpath\".",
"WARN: filename.mk:13: \"-DMACRO\" is a compiler flag "+
"and belongs on CFLAGS, CPPFLAGS, CXXFLAGS or FFLAGS instead of LDFLAGS.",
"WARN: filename.mk:14: \"-UMACRO\" is a compiler flag "+
"and belongs on CFLAGS, CPPFLAGS, CXXFLAGS or FFLAGS instead of LDFLAGS.",
"WARN: filename.mk:15: \"-P\" is a compiler flag "+
"and belongs on CFLAGS, CPPFLAGS, CXXFLAGS or FFLAGS instead of LDFLAGS.",
"WARN: filename.mk:16: \"-E\" is a compiler flag "+
"and belongs on CFLAGS, CPPFLAGS, CXXFLAGS or FFLAGS instead of LDFLAGS.",
"WARN: filename.mk:17: \"-I${PREFIX}/include\" is a compiler flag "+
"and belongs on CFLAGS, CPPFLAGS, CXXFLAGS or FFLAGS instead of LDFLAGS.")
}
func (s *Suite) Test_VartypeCheck_License(c *check.C) {
t := s.Init(c)
t.SetUpPkgsrc() // Adds the gnu-gpl-v2 and 2-clause-bsd licenses
t.SetUpPackage("category/package")
t.FinishSetUp()
G.Pkg = NewPackage(t.File("category/package"))
mklines := t.NewMkLines("perl5.mk",
MkCvsID,
"PERL5_LICENSE= gnu-gpl-v2 OR artistic")
// Also registers the PERL5_LICENSE variable in the package.
mklines.collectDefinedVariables()
vt := NewVartypeCheckTester(t, (*VartypeCheck).License)
vt.Varname("LICENSE")
vt.Values(
"gnu-gpl-v2",
"AND mit",
"${PERL5_LICENSE}", // Is properly resolved, see perl5.mk above.
"${UNKNOWN_LICENSE}")
vt.Output(
"ERROR: filename.mk:2: Parse error for license condition \"AND mit\".",
"WARN: filename.mk:3: License file ~/licenses/artistic does not exist.",
"ERROR: filename.mk:4: Parse error for license condition \"${UNKNOWN_LICENSE}\".")
vt.Op(opAssignAppend)
vt.Values(
"gnu-gpl-v2",
"AND mit")
vt.Output(
"ERROR: filename.mk:11: Parse error for appended license condition \"gnu-gpl-v2\".",
"WARN: filename.mk:12: License file ~/licenses/mit does not exist.")
}
func (s *Suite) Test_VartypeCheck_MachineGnuPlatform(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).MachineGnuPlatform)
vt.Varname("MACHINE_GNU_PLATFORM")
vt.Op(opUseMatch)
vt.Values(
"x86_64-pc-cygwin",
"Cygwin-*-amd64",
"x86_64-*",
"*-*-*-*",
"${OTHER_VAR}",
"x86_64-pc") // Just for code coverage.
vt.Output(
"WARN: filename.mk:2: The pattern \"Cygwin\" cannot match any of "+
"{ aarch64 aarch64_be alpha amd64 arc arm armeb armv4 armv4eb armv6 armv6eb armv7 armv7eb "+
"cobalt convex dreamcast hpcmips hpcsh hppa hppa64 i386 i486 ia64 m5407 m68010 m68k m88k "+
"mips mips64 mips64el mipseb mipsel mipsn32 mlrisc ns32k pc532 pmax powerpc powerpc64 "+
"rs6000 s390 sh shle sparc sparc64 vax x86_64 "+
"} for the hardware architecture part of MACHINE_GNU_PLATFORM.",
"WARN: filename.mk:2: The pattern \"amd64\" cannot match any of "+
"{ bitrig bsdos cygwin darwin dragonfly freebsd haiku hpux interix irix linux mirbsd "+
"netbsd openbsd osf1 solaris sunos } "+
"for the operating system part of MACHINE_GNU_PLATFORM.",
"WARN: filename.mk:4: \"*-*-*-*\" is not a valid platform pattern.",
"WARN: filename.mk:6: \"x86_64-pc\" is not a valid platform pattern.")
}
func (s *Suite) Test_VartypeCheck_MachinePlatformPattern(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).MachinePlatformPattern)
vt.Varname("ONLY_FOR_PLATFORM")
vt.Op(opUseMatch)
vt.Values(
"linux-i386",
"nextbsd-5.0-8087",
"netbsd-7.0-l*",
"NetBSD-1.6.2-i386",
"FreeBSD*",
"FreeBSD-*",
"${LINUX}",
"NetBSD-[0-1]*-*")
vt.Output(
"WARN: filename.mk:1: \"linux-i386\" is not a valid platform pattern.",
"WARN: filename.mk:2: The pattern \"nextbsd\" cannot match any of "+
"{ AIX BSDOS Bitrig Cygwin Darwin DragonFly FreeBSD FreeMiNT GNUkFreeBSD HPUX Haiku "+
"IRIX Interix Linux Minix MirBSD NetBSD OSF1 OpenBSD QNX SCO_SV SunOS UnixWare "+
"} for the operating system part of ONLY_FOR_PLATFORM.",
"WARN: filename.mk:2: The pattern \"8087\" cannot match any of "+
"{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 "+
"cobalt coldfire convex dreamcast "+
"earm earmeb earmhf earmhfeb earmv4 earmv4eb "+
"earmv5 earmv5eb earmv6 earmv6eb earmv6hf "+
"earmv6hfeb earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 "+
"i386 i586 i686 ia64 m68000 m68k m88k "+
"mips mips64 mips64eb mips64el mipseb mipsel mipsn32 "+
"mlrisc ns32k pc532 pmax powerpc powerpc64 "+
"rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+
"} for the hardware architecture part of ONLY_FOR_PLATFORM.",
"WARN: filename.mk:3: The pattern \"netbsd\" cannot match any of "+
"{ AIX BSDOS Bitrig Cygwin Darwin DragonFly FreeBSD FreeMiNT GNUkFreeBSD HPUX Haiku "+
"IRIX Interix Linux Minix MirBSD NetBSD OSF1 OpenBSD QNX SCO_SV SunOS UnixWare "+
"} for the operating system part of ONLY_FOR_PLATFORM.",
"WARN: filename.mk:3: The pattern \"l*\" cannot match any of "+
"{ aarch64 aarch64eb alpha amd64 arc arm arm26 arm32 "+
"cobalt coldfire convex dreamcast "+
"earm earmeb earmhf earmhfeb earmv4 earmv4eb "+
"earmv5 earmv5eb earmv6 earmv6eb earmv6hf "+
"earmv6hfeb earmv7 earmv7eb earmv7hf earmv7hfeb evbarm hpcmips hpcsh hppa hppa64 "+
"i386 i586 i686 ia64 m68000 m68k m88k "+
"mips mips64 mips64eb mips64el mipseb mipsel mipsn32 "+
"mlrisc ns32k pc532 pmax powerpc powerpc64 "+
"rs6000 s390 sh3eb sh3el sparc sparc64 vax x86_64 "+
"} for the hardware architecture part of ONLY_FOR_PLATFORM.",
"WARN: filename.mk:5: \"FreeBSD*\" is not a valid platform pattern.",
"WARN: filename.mk:8: Please use \"[0-1].*\" instead of \"[0-1]*\" as the version pattern.")
}
func (s *Suite) Test_VartypeCheck_MailAddress(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).MailAddress)
vt.Varname("MAINTAINER")
vt.Values(
"pkgsrc-users@netbsd.org",
"tech-pkg@NetBSD.org",
"packages@NetBSD.org",
"user1@example.org,user2@example.org")
vt.Output(
"WARN: filename.mk:1: Please write \"NetBSD.org\" instead of \"netbsd.org\".",
"ERROR: filename.mk:2: This mailing list address is obsolete. Use pkgsrc-users@NetBSD.org instead.",
"ERROR: filename.mk:3: This mailing list address is obsolete. Use pkgsrc-users@NetBSD.org instead.",
"WARN: filename.mk:4: \"user1@example.org,user2@example.org\" is not a valid mail address.")
}
func (s *Suite) Test_VartypeCheck_Message(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Message)
vt.Varname("SUBST_MESSAGE.id")
vt.Values(
"\"Correct paths\"",
"Correct paths")
vt.Output(
"WARN: filename.mk:1: SUBST_MESSAGE.id should not be quoted.")
}
func (s *Suite) Test_VartypeCheck_Option(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Option)
G.Pkgsrc.PkgOptions["documented"] = "Option description"
G.Pkgsrc.PkgOptions["undocumented"] = ""
vt.Varname("PKG_OPTIONS.pkgbase")
vt.Values(
"documented",
"undocumented",
"unknown",
"underscore_is_deprecated",
"UPPER")
vt.Output(
"WARN: filename.mk:3: Unknown option \"unknown\".",
"WARN: filename.mk:4: Use of the underscore character in option names is deprecated.",
"ERROR: filename.mk:5: Invalid option name \"UPPER\". "+
"Option names must start with a lowercase letter and be all-lowercase.")
}
func (s *Suite) Test_VartypeCheck_Pathlist(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Pathlist)
vt.Varname("PATH")
vt.Values(
"/usr/bin:/usr/sbin:.::${LOCALBASE}/bin:${HOMEPAGE:S,https://,,}:${TMPDIR}:${PREFIX}/!!!",
"/directory with spaces")
vt.Output(
"ERROR: filename.mk:1: The component \".\" of PATH must be an absolute path.",
"ERROR: filename.mk:1: The component \"\" of PATH must be an absolute path.",
"WARN: filename.mk:1: \"${PREFIX}/!!!\" is not a valid pathname.",
"WARN: filename.mk:2: \"/directory with spaces\" is not a valid pathname.")
}
func (s *Suite) Test_VartypeCheck_PathMask(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).PathMask)
vt.Varname("DISTDIRS")
vt.Values(
"/home/user/*",
"src/*&*",
"src/*/*")
vt.Output(
"WARN: filename.mk:2: The pathname pattern \"src/*&*\" contains the invalid character \"&\".")
vt.Op(opUseMatch)
vt.Values("any")
vt.OutputEmpty()
}
func (s *Suite) Test_VartypeCheck_Pathname(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Pathname)
vt.Varname("EGDIR")
vt.Values(
"${PREFIX}/*",
"${PREFIX}/share/locale",
"share/locale",
"/bin")
vt.Op(opUseMatch)
vt.Values(
"anything")
vt.Output(
"WARN: filename.mk:1: The pathname \"${PREFIX}/*\" contains the invalid character \"*\".")
}
func (s *Suite) Test_VartypeCheck_Perl5Packlist(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Perl5Packlist)
vt.Varname("PERL5_PACKLIST")
vt.Values(
"${PKGBASE}",
"anything else")
vt.Output(
"WARN: filename.mk:1: PERL5_PACKLIST should not depend on other variables.")
}
func (s *Suite) Test_VartypeCheck_Perms(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Perms)
vt.Varname("CONF_FILES_PERMS")
vt.Op(opAssignAppend)
vt.Values(
"root",
"${ROOT_USER}",
"ROOT_USER",
"${REAL_ROOT_USER}",
"${ROOT_GROUP}",
"${REAL_ROOT_GROUP}")
vt.Output(
"ERROR: filename.mk:2: ROOT_USER must not be used in permission definitions. Use REAL_ROOT_USER instead.",
"ERROR: filename.mk:5: ROOT_GROUP must not be used in permission definitions. Use REAL_ROOT_GROUP instead.")
}
func (s *Suite) Test_VartypeCheck_Pkgname(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Pkgname)
vt.Varname("PKGNAME")
vt.Values(
"pkgbase-0",
"pkgbase-1.0",
"pkgbase-1.1234567890",
"pkgbase-1z",
"pkgbase-client-11a",
"pkgbase-client-1.a",
"pkgbase-client-1_20180101",
"pkgbase-z1",
"pkgbase-3.1.4.1.5.9.2.6.5.3.5.8.9.7.9")
vt.Output(
"WARN: filename.mk:8: \"pkgbase-z1\" is not a valid package name.")
vt.Op(opUseMatch)
vt.Values(
"pkgbase-[0-9]*")
vt.OutputEmpty()
}
func (s *Suite) Test_VartypeCheck_PkgOptionsVar(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).PkgOptionsVar)
vt.Varname("PKG_OPTIONS_VAR.screen")
vt.Values(
"PKG_OPTIONS.${PKGBASE}",
"PKG_OPTIONS.anypkgbase",
"PKG_OPTS.mc")
vt.Output(
"ERROR: filename.mk:1: PKGBASE must not be used in PKG_OPTIONS_VAR.",
"ERROR: filename.mk:3: PKG_OPTIONS_VAR must be "+
"of the form \"PKG_OPTIONS.*\", not \"PKG_OPTS.mc\".")
}
func (s *Suite) Test_VartypeCheck_PkgPath(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).PkgPath)
t.CreateFileLines("category/other-package/Makefile")
t.Chdir("category/package")
vt.Varname("PKGPATH")
vt.Values(
"category/other-package",
"${OTHER_VAR}",
"invalid",
"../../invalid/relative")
vt.Output(
"ERROR: filename.mk:3: Relative path \"../../invalid/Makefile\" does not exist.",
"WARN: filename.mk:3: \"../../invalid\" is not a valid relative package directory.",
"ERROR: filename.mk:4: Relative path \"../../../../invalid/relative/Makefile\" does not exist.",
"WARN: filename.mk:4: \"../../../../invalid/relative\" is not a valid relative package directory.")
}
func (s *Suite) Test_VartypeCheck_PkgRevision(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).PkgRevision)
vt.Varname("PKGREVISION")
vt.Values(
"3a")
vt.Output(
"WARN: filename.mk:1: PKGREVISION must be a positive integer number.",
"ERROR: filename.mk:1: PKGREVISION only makes sense directly in the package Makefile.")
vt.File("Makefile")
vt.Values(
"3")
vt.OutputEmpty()
}
func (s *Suite) Test_VartypeCheck_PythonDependency(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).PythonDependency)
vt.Varname("PYTHON_VERSIONED_DEPENDENCIES")
vt.Values(
"cairo",
"${PYDEP}",
"cairo,X")
vt.Output(
"WARN: filename.mk:2: Python dependencies should not contain variables.",
"WARN: filename.mk:3: Invalid Python dependency \"cairo,X\".")
}
func (s *Suite) Test_VartypeCheck_PrefixPathname(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).PrefixPathname)
vt.Varname("PKGMANDIR")
vt.Values(
"man/man1",
"share/locale")
vt.Output(
"WARN: filename.mk:1: Please use \"${PKGMANDIR}/man1\" instead of \"man/man1\".")
}
func (s *Suite) Test_VartypeCheck_RelativePkgPath(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).RelativePkgPath)
t.CreateFileLines("category/other-package/Makefile")
t.Chdir("category/package")
vt.Varname("DISTINFO_FILE")
vt.Values(
"category/other-package",
"../../category/other-package",
"${OTHER_VAR}",
"invalid",
"../../invalid/relative")
vt.Output(
"ERROR: filename.mk:1: Relative path \"category/other-package\" does not exist.",
"ERROR: filename.mk:4: Relative path \"invalid\" does not exist.",
"ERROR: filename.mk:5: Relative path \"../../invalid/relative\" does not exist.")
}
func (s *Suite) Test_VartypeCheck_Restricted(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Restricted)
vt.Varname("NO_BIN_ON_CDROM")
vt.Values(
"May only be distributed free of charge")
vt.Output(
"WARN: filename.mk:1: The only valid value for NO_BIN_ON_CDROM is ${RESTRICTED}.")
}
func (s *Suite) Test_VartypeCheck_SedCommands(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).SedCommands)
vt.Varname("SUBST_SED.dummy")
vt.Values(
"s,@COMPILER@,gcc,g",
"-e s,a,b, -e a,b,c,",
"-e \"s,#,comment ,\"",
"-e \"s,\\#,comment ,\"",
"-E",
"-n",
"-e 1d",
"1d",
"-e",
"-i s,from,to,",
"-e s,$${unclosedShellVar") // Just for code coverage.
vt.Output(
"NOTE: filename.mk:1: Please always use \"-e\" in sed commands, even if there is only one substitution.",
"NOTE: filename.mk:2: Each sed command should appear in an assignment of its own.",
"WARN: filename.mk:3: The # character starts a Makefile comment.",
"ERROR: filename.mk:3: Invalid shell words \"\\\"s,\" in sed commands.",
"WARN: filename.mk:8: Unknown sed command \"1d\".",
"ERROR: filename.mk:9: The -e option to sed requires an argument.",
"WARN: filename.mk:10: Unknown sed command \"-i\".",
"NOTE: filename.mk:10: Please always use \"-e\" in sed commands, even if there is only one substitution.",
// TODO: duplicate warning
"WARN: filename.mk:11: Unclosed shell variable starting at \"$${unclosedShellVar\".",
"WARN: filename.mk:11: Unclosed shell variable starting at \"$${unclosedShellVar\".")
}
func (s *Suite) Test_VartypeCheck_ShellCommand(c *check.C) {
t := s.Init(c)
t.SetUpVartypes()
vt := NewVartypeCheckTester(t, (*VartypeCheck).ShellCommand)
vt.Varname("INSTALL_CMD")
vt.Values(
"${INSTALL_DATA} -m 0644 ${WRKDIR}/source ${DESTDIR}${PREFIX}/target")
vt.Op(opUseMatch)
vt.Values("*")
vt.OutputEmpty()
vt.Varname("CC")
vt.Op(opAssignAppend)
vt.Values("-ggdb")
vt.OutputEmpty()
}
func (s *Suite) Test_VartypeCheck_ShellCommands(c *check.C) {
t := s.Init(c)
t.SetUpVartypes()
t.SetUpTool("echo", "ECHO", AtRunTime)
vt := NewVartypeCheckTester(t, (*VartypeCheck).ShellCommands)
vt.Varname("GENERATE_PLIST")
vt.Values(
"echo bin/program",
"echo bin/program;")
vt.Output(
"WARN: filename.mk:1: This shell command list should end with a semicolon.")
}
func (s *Suite) Test_VartypeCheck_Stage(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Stage)
vt.Varname("SUBST_STAGE.dummy")
vt.Values(
"post-patch",
"post-modern",
"pre-test")
vt.Output(
"WARN: filename.mk:2: Invalid stage name \"post-modern\". " +
"Use one of {pre,do,post}-{extract,patch,configure,build,test,install}.")
}
func (s *Suite) Test_VartypeCheck_Tool(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).Tool)
t.SetUpTool("tool1", "", AtRunTime)
t.SetUpTool("tool2", "", AtRunTime)
t.SetUpTool("tool3", "", AtRunTime)
vt.Varname("USE_TOOLS")
vt.Op(opAssignAppend)
vt.Values(
"tool3:run",
"tool2:unknown",
"${t}",
"mal:formed:tool",
"unknown")
vt.Output(
"ERROR: filename.mk:2: Invalid tool dependency \"unknown\". "+
"Use one of \"bootstrap\", \"build\", \"pkgsrc\", \"run\" or \"test\".",
"ERROR: filename.mk:4: Invalid tool dependency \"mal:formed:tool\".",
"ERROR: filename.mk:5: Unknown tool \"unknown\".")
vt.Varname("USE_TOOLS.NetBSD")
vt.Op(opAssignAppend)
vt.Values(
"tool3:run",
"tool2:unknown")
vt.Output(
"ERROR: filename.mk:12: Invalid tool dependency \"unknown\". " +
"Use one of \"bootstrap\", \"build\", \"pkgsrc\", \"run\" or \"test\".")
vt.Varname("TOOLS_NOOP")
vt.Op(opAssignAppend)
vt.Values(
"gmake:run")
vt.Varname("TOOLS_NOOP")
vt.Op(opAssign) // TODO: In a Makefile, this should be equivalent to opAssignAppend.
vt.Values(
"gmake:run")
vt.Output(
"ERROR: filename.mk:31: Unknown tool \"gmake\".")
vt.Varname("USE_TOOLS")
vt.Op(opUseMatch)
vt.Values(
"tool1",
"tool1\\:build",
"tool1\\:*",
"${t}\\:build")
vt.OutputEmpty()
}
func (s *Suite) Test_VartypeCheck_URL(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).URL)
vt.Varname("HOMEPAGE")
vt.Values(
"# none",
"${OTHER_VAR}",
"https://www.NetBSD.org/",
"https://www.netbsd.org/",
"https://www.example.org",
"ftp://example.org/pub/",
"gopher://example.org/")
vt.Output(
"WARN: filename.mk:4: Please write NetBSD.org instead of www.netbsd.org.",
"NOTE: filename.mk:5: For consistency, please add a trailing slash to \"https://www.example.org\".")
vt.Values(
"",
"ftp://example.org/<",
"gopher://example.org/<",
"http://example.org/<",
"https://example.org/<",
"https://www.example.org/path with spaces",
"httpxs://www.example.org",
"mailto:someone@example.org",
"string with spaces")
vt.Output(
"WARN: filename.mk:11: \"\" is not a valid URL.",
"WARN: filename.mk:12: \"ftp://example.org/<\" is not a valid URL.",
"WARN: filename.mk:13: \"gopher://example.org/<\" is not a valid URL.",
"WARN: filename.mk:14: \"http://example.org/<\" is not a valid URL.",
"WARN: filename.mk:15: \"https://example.org/<\" is not a valid URL.",
"WARN: filename.mk:16: \"https://www.example.org/path with spaces\" is not a valid URL.",
"WARN: filename.mk:17: \"httpxs://www.example.org\" is not a valid URL. Only ftp, gopher, http, and https URLs are allowed here.",
"WARN: filename.mk:18: \"mailto:someone@example.org\" is not a valid URL.",
"WARN: filename.mk:19: \"string with spaces\" is not a valid URL.")
// Yes, even in 2019, some pkgsrc-wip packages really use a gopher HOMEPAGE.
vt.Values(
"gopher://bitreich.org/1/scm/geomyidae")
vt.OutputEmpty()
}
func (s *Suite) Test_VartypeCheck_UserGroupName(c *check.C) {
t := s.Init(c)
vt := NewVartypeCheckTester(t, (*VartypeCheck).UserGroupName)
vt.Varname("APACHE_USER")
vt.Values(
"user with spaces",
"typical_username",
"user123",
"domain\\user",
"${OTHER_VAR}")
vt.Output(
"WARN: filename.mk:1: Invalid user or group name \"user with spaces\".",
"WARN: filename.mk:4: Invalid user or group name \"domain\\\\user\".")
}
func (s *Suite) Test_VartypeCheck_VariableName(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).VariableName)
vt.Varname("BUILD_DEFS")
vt.Values(
"VARBASE",
"VarBase",
"PKG_OPTIONS_VAR.pkgbase",
"${INDIRECT}")
vt.Output(
"WARN: filename.mk:2: \"VarBase\" is not a valid variable name.")
}
func (s *Suite) Test_VartypeCheck_VariableNamePattern(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).VariableNamePattern)
vt.Varname("_SORTED_VARS.group")
vt.Values(
"VARBASE",
"VarBase",
"PKG_OPTIONS_VAR.pkgbase",
"${INDIRECT}",
"*_DIRS",
"VAR.*",
"***")
vt.Output(
"WARN: filename.mk:2: \"VarBase\" is not a valid variable name pattern.")
}
func (s *Suite) Test_VartypeCheck_Version(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Version)
vt.Varname("PERL5_REQD")
vt.Op(opAssignAppend)
vt.Values(
"0",
"1.2.3.4.5.6",
"4.1nb17",
"4.1-SNAPSHOT",
"4pre7",
"${VER}")
vt.Output(
"WARN: filename.mk:4: Invalid version number \"4.1-SNAPSHOT\".")
vt.Op(opUseMatch)
vt.Values(
"a*",
"1.2/456",
"4*",
"?.??",
"1.[234]*",
"1.[2-7].*",
"[0-9]*")
vt.Output(
"WARN: filename.mk:11: Invalid version number pattern \"a*\".",
"WARN: filename.mk:12: Invalid version number pattern \"1.2/456\".",
"WARN: filename.mk:13: Please use \"4.*\" instead of \"4*\" as the version pattern.",
"WARN: filename.mk:15: Please use \"1.[234].*\" instead of \"1.[234]*\" as the version pattern.")
}
func (s *Suite) Test_VartypeCheck_WrapperReorder(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).WrapperReorder)
vt.Varname("WRAPPER_REORDER")
vt.Op(opAssignAppend)
vt.Values(
"reorder:l:first:second",
"reorder:l:first",
"omit:first")
vt.Output(
"WARN: filename.mk:2: Unknown wrapper reorder command \"reorder:l:first\".",
"WARN: filename.mk:3: Unknown wrapper reorder command \"omit:first\".")
}
func (s *Suite) Test_VartypeCheck_WrapperTransform(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).WrapperTransform)
vt.Varname("WRAPPER_TRANSFORM_CMDS")
vt.Op(opAssignAppend)
vt.Values(
"rm:-O3",
"opt:-option",
"rename:src:dst",
"rm-optarg:-option",
"rmdir:/usr/include",
"rpath:/usr/lib:/usr/pkg/lib",
"rpath:/usr/lib",
"unknown",
"-e 's,-Wall,-Wall -Wextra,'")
vt.Output(
"WARN: filename.mk:7: Unknown wrapper transform command \"rpath:/usr/lib\".",
"WARN: filename.mk:8: Unknown wrapper transform command \"unknown\".")
}
func (s *Suite) Test_VartypeCheck_WrksrcSubdirectory(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).WrksrcSubdirectory)
vt.Varname("BUILD_DIRS")
vt.Op(opAssignAppend)
vt.Values(
"${WRKSRC}",
"${WRKSRC}/",
"${WRKSRC}/.",
"${WRKSRC}/subdir",
"${CONFIGURE_DIRS}",
"${WRKSRC}/directory with spaces", // This is a list of 3 directories.
"directory with spaces", // This is a list of 3 directories.
"../other",
"${WRKDIR}/sub",
"${SRCDIR}/sub")
vt.Output(
"NOTE: filename.mk:1: You can use \".\" instead of \"${WRKSRC}\".",
"NOTE: filename.mk:2: You can use \".\" instead of \"${WRKSRC}/\".",
"NOTE: filename.mk:3: You can use \".\" instead of \"${WRKSRC}/.\".",
"NOTE: filename.mk:4: You can use \"subdir\" instead of \"${WRKSRC}/subdir\".",
"NOTE: filename.mk:6: You can use \"directory\" instead of \"${WRKSRC}/directory\".",
"WARN: filename.mk:8: \"../other\" is not a valid subdirectory of ${WRKSRC}.",
"WARN: filename.mk:9: \"${WRKDIR}/sub\" is not a valid subdirectory of ${WRKSRC}.",
"WARN: filename.mk:10: \"${SRCDIR}/sub\" is not a valid subdirectory of ${WRKSRC}.")
}
func (s *Suite) Test_VartypeCheck_Yes(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).Yes)
vt.Varname("APACHE_MODULE")
vt.Values(
"yes",
"no",
"${YESVAR}")
vt.Output(
"WARN: filename.mk:2: APACHE_MODULE should be set to YES or yes.",
"WARN: filename.mk:3: APACHE_MODULE should be set to YES or yes.")
vt.Varname("PKG_DEVELOPER")
vt.Op(opUseMatch)
vt.Values(
"yes",
"no",
"${YESVAR}")
vt.Output(
"WARN: filename.mk:11: PKG_DEVELOPER should only be used in a \".if defined(...)\" condition.",
"WARN: filename.mk:12: PKG_DEVELOPER should only be used in a \".if defined(...)\" condition.",
"WARN: filename.mk:13: PKG_DEVELOPER should only be used in a \".if defined(...)\" condition.")
}
func (s *Suite) Test_VartypeCheck_YesNo(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).YesNo)
vt.Varname("GNU_CONFIGURE")
vt.Values(
"yes",
"no",
"ja",
"${YESVAR}")
vt.Output(
"WARN: filename.mk:3: GNU_CONFIGURE should be set to YES, yes, NO, or no.",
"WARN: filename.mk:4: GNU_CONFIGURE should be set to YES, yes, NO, or no.")
}
func (s *Suite) Test_VartypeCheck_YesNoIndirectly(c *check.C) {
vt := NewVartypeCheckTester(s.Init(c), (*VartypeCheck).YesNoIndirectly)
vt.Varname("GNU_CONFIGURE")
vt.Values(
"yes",
"no",
"ja",
"${YESVAR}")
vt.Output(
"WARN: filename.mk:3: GNU_CONFIGURE should be set to YES, yes, NO, or no.")
}
// VartypeCheckTester helps to test the many different checks in VartypeCheck.
// It keeps track of the current variable, operator, filename, line number,
// so that the test can focus on the interesting details.
type VartypeCheckTester struct {
tester *Tester
checker func(cv *VartypeCheck)
filename string
lineno int
varname string
op MkOperator
}
// NewVartypeCheckTester starts the test with a filename of "filename", at line 1,
// with "=" as the operator. The variable has to be initialized explicitly.
func NewVartypeCheckTester(t *Tester, checker func(cv *VartypeCheck)) *VartypeCheckTester {
// This is necessary to know whether the variable name is a list type
// since in such a case each value is split into the list elements.
if G.Pkgsrc.VariableType(nil, "USE_CWRAPPERS") == nil {
t.SetUpVartypes()
}
return &VartypeCheckTester{
t,
checker,
"filename.mk",
1,
"",
opAssign}
}
func (vt *VartypeCheckTester) Varname(varname string) {
vt.varname = varname
vt.nextSection()
}
func (vt *VartypeCheckTester) File(filename string) {
vt.filename = filename
vt.lineno = 1
}
// Op sets the operator for the following tests.
// The line number is advanced to the next number ending in 1, e.g. 11, 21, 31.
func (vt *VartypeCheckTester) Op(op MkOperator) {
vt.op = op
vt.nextSection()
}
// Values feeds each of the values to the actual check.
// Each value is interpreted as if it were written verbatim into a Makefile line.
// That is, # starts a comment.
//
// For the opUseMatch operator, all colons and closing braces must be escaped.
func (vt *VartypeCheckTester) Values(values ...string) {
toText := func(value string) string {
op := vt.op
opStr := op.String()
varname := vt.varname
if op == opUseMatch {
return sprintf(".if ${%s:M%s} == \"\"", varname, value)
}
if !contains(opStr, "=") {
panic("Invalid operator: " + opStr)
}
space := ifelseStr(hasSuffix(varname, "+") && opStr == "=", " ", "")
return varname + space + opStr + value
}
test := func(mklines *MkLines, mkline *MkLine, value string) {
varname := vt.varname
comment := ""
if mkline.IsVarassign() {
mkline.Tokenize(value, true) // Produce some warnings as side-effects.
comment = mkline.VarassignComment()
}
effectiveValue := value
if mkline.IsVarassign() {
effectiveValue = mkline.Value()
}
vartype := G.Pkgsrc.VariableType(nil, varname)
// See MkLineChecker.checkVartype.
var lineValues []string
if vartype == nil || !vartype.List() {
lineValues = []string{effectiveValue}
} else {
lineValues = mkline.ValueFields(effectiveValue)
}
for _, lineValue := range lineValues {
valueNovar := mkline.WithoutMakeVariables(lineValue)
vc := VartypeCheck{mklines, mkline, varname, vt.op, lineValue, valueNovar, comment, false}
vt.checker(&vc)
}
}
for _, value := range values {
text := toText(value)
line := vt.tester.NewLine(vt.filename, vt.lineno, text)
mklines := NewMkLines(NewLines(vt.filename, []*Line{line}))
vt.lineno++
mklines.ForEach(func(mkline *MkLine) { test(mklines, mkline, value) })
}
}
// Output checks that the output from all previous steps is
// the same as the expectedLines.
func (vt *VartypeCheckTester) Output(expectedLines ...string) {
vt.tester.CheckOutputLines(expectedLines...)
vt.nextSection()
}
func (vt *VartypeCheckTester) OutputEmpty() {
vt.tester.CheckOutputEmpty()
vt.nextSection()
}
func (vt *VartypeCheckTester) nextSection() {
if vt.lineno%10 != 1 {
vt.lineno = vt.lineno - vt.lineno%10 + 11
}
}