Updated pkglint to 5.3.5 Changes since 5.3.4: * Added parser for Makefile conditionals * Variables that are matched using the :M modifier are checked whether the matched value is sensible * Reworded and explained warning for variable ordering in packages * Fixed bug in Tree.String * Fixed a few variable typesdiff -r1.480 -r1.481 pkgsrc/pkgtools/pkglint/Makefile
(rillig)
@@ -1,16 +1,16 @@ | @@ -1,16 +1,16 @@ | |||
1 | # $NetBSD: Makefile,v 1.480 2016/01/27 21:55:50 rillig Exp $ | 1 | # $NetBSD: Makefile,v 1.481 2016/01/31 17:18:54 rillig Exp $ | |
2 | 2 | |||
3 | PKGNAME= pkglint-5.3.4 | 3 | PKGNAME= pkglint-5.3.5 | |
4 | DISTFILES= # none | 4 | DISTFILES= # none | |
5 | CATEGORIES= pkgtools | 5 | CATEGORIES= pkgtools | |
6 | 6 | |||
7 | OWNER= rillig@NetBSD.org | 7 | OWNER= rillig@NetBSD.org | |
8 | HOMEPAGE= http://www.NetBSD.org/docs/pkgsrc/ | 8 | HOMEPAGE= http://www.NetBSD.org/docs/pkgsrc/ | |
9 | COMMENT= Verifier for NetBSD packages | 9 | COMMENT= Verifier for NetBSD packages | |
10 | LICENSE= 2-clause-bsd | 10 | LICENSE= 2-clause-bsd | |
11 | CONFLICTS+= pkglint4-[0-9]* | 11 | CONFLICTS+= pkglint4-[0-9]* | |
12 | 12 | |||
13 | WRKSRC= ${WRKDIR}/netbsd.org/pkglint | 13 | WRKSRC= ${WRKDIR}/netbsd.org/pkglint | |
14 | NO_CHECKSUM= yes | 14 | NO_CHECKSUM= yes | |
15 | USE_LANGUAGES= # none | 15 | USE_LANGUAGES= # none | |
16 | USE_TOOLS+= pax | 16 | USE_TOOLS+= pax |
@@ -1,31 +1,28 @@ | @@ -1,31 +1,28 @@ | |||
1 | $NetBSD: TODO,v 1.83 2015/11/25 13:29:07 rillig Exp $ | 1 | $NetBSD: TODO,v 1.84 2016/01/31 17:18:54 rillig Exp $ | |
2 | 2 | |||
3 | Please add your own entries at the bottom of this file. If possible, | 3 | Please add your own entries at the bottom of this file. If possible, | |
4 | include the name of an example package where a warning should occur. | 4 | include the name of an example package where a warning should occur. | |
5 | 5 | |||
6 | * warn about the use of ${WRKDIR:=...}, as this construct should only | 6 | * warn about the use of ${WRKDIR:=...}, as this construct should only | |
7 | be used with lists. | 7 | be used with lists. | |
8 | * Add checks for binary packages. See Debian/lintian for ideas. | |||
9 | * Of the user-defined variables, some may be used at load-time and some | 8 | * Of the user-defined variables, some may be used at load-time and some | |
10 | don't. Find out how pkglint can distinguish them. | 9 | don't. Find out how pkglint can distinguish them. | |
11 | * Make sure that no variable is modified at load-time after it has been | 10 | * Make sure that no variable is modified at load-time after it has been | |
12 | used once. This should at least flag BUILD_DEFS in bsd.pkg.mk. | 11 | used once. This should at least flag BUILD_DEFS in bsd.pkg.mk. | |
13 | * ${MACHINE_ARCH}-${LOWER_OPSYS}elf in PLISTs etc. is a NetBSD config.guess | 12 | * ${MACHINE_ARCH}-${LOWER_OPSYS}elf in PLISTs etc. is a NetBSD config.guess | |
14 | problem ==> use of ${APPEND_ELF} | 13 | problem ==> use of ${APPEND_ELF} | |
15 | * Packages including lang/python/extension.mk must follow the Python version | 14 | * Packages including lang/python/extension.mk must follow the Python version | |
16 | scheme. Enforcing PYPKGPREFIX for those is most likely a good idea. | 15 | scheme. Enforcing PYPKGPREFIX for those is most likely a good idea. | |
17 | * Check for parallel files/dirs whose names differ only in case. | 16 | * Check for parallel files/dirs whose names differ only in case. | |
18 | * If a dependency depends on an option (in options.mk), it should also | 17 | * If a dependency depends on an option (in options.mk), it should also | |
19 | depend on the same option in the buildlink3.mk file. | 18 | depend on the same option in the buildlink3.mk file. | |
20 | * Complain about ${PKGSRC_COMPILER} == "sunpro", which should be | 19 | * Complain about ${PKGSRC_COMPILER} == "sunpro", which should be | |
21 | !empty(PKGSRC_COMPILER:Msunpro). | 20 | !empty(PKGSRC_COMPILER:Msunpro). | |
22 | * If USE_TOOLS has autoconf213, and the package does stuff like | |||
23 | cd ${WRKSRC} && autoconf, then an incorrect warning is issued. | |||
24 | * don't complain about "procedure calls", like for pkg-build-options in | 21 | * don't complain about "procedure calls", like for pkg-build-options in | |
25 | the various buildlink3.mk files. | 22 | the various buildlink3.mk files. | |
26 | * if package A conflicts with B, then B should also conflict with A. | 23 | * if package A conflicts with B, then B should also conflict with A. | |
27 | * When pkglint runs on a case-insensitive filesystem, it should still | 24 | * When pkglint runs on a case-insensitive filesystem, it should still | |
28 | point out problems that only occur on case-sensitive filesystems. For | 25 | point out problems that only occur on case-sensitive filesystems. For | |
29 | example, devel/p5-Net-LDAP and devel/p5-Net-ldap should be considered | 26 | example, devel/p5-Net-LDAP and devel/p5-Net-ldap should be considered | |
30 | different paths. | 27 | different paths. | |
31 | * Warn about using REPLACE_PYTHON without including application.mk. | 28 | * Warn about using REPLACE_PYTHON without including application.mk. |
@@ -522,15 +522,18 @@ func (gd *GlobalData) loadDeprecatedVars | @@ -522,15 +522,18 @@ func (gd *GlobalData) loadDeprecatedVars | |||
522 | "NO_MTREE": "You can just remove it.", | 522 | "NO_MTREE": "You can just remove it.", | |
523 | 523 | |||
524 | // July 2012 | 524 | // July 2012 | |
525 | "SETGIDGAME": "Use USE_GAMESGROUP instead.", | 525 | "SETGIDGAME": "Use USE_GAMESGROUP instead.", | |
526 | "GAMEGRP": "Use GAMES_GROUP instead.", | 526 | "GAMEGRP": "Use GAMES_GROUP instead.", | |
527 | "GAMEOWN": "Use GAMES_USER instead.", | 527 | "GAMEOWN": "Use GAMES_USER instead.", | |
528 | 528 | |||
529 | // July 2013 | 529 | // July 2013 | |
530 | "USE_GNU_READLINE": "Include \"../../devel/readline/buildlink3.mk\" instead.", | 530 | "USE_GNU_READLINE": "Include \"../../devel/readline/buildlink3.mk\" instead.", | |
531 | 531 | |||
532 | // October 2014 | 532 | // October 2014 | |
533 | "SVR4_PKGNAME": "Just remove it.", | 533 | "SVR4_PKGNAME": "Just remove it.", | |
534 | "PKG_INSTALLATION_TYPES": "Just remove it.", | 534 | "PKG_INSTALLATION_TYPES": "Just remove it.", | |
535 | ||||
536 | // January 2016 | |||
537 | "SUBST_POSTCMD.*": "Has been removed, as it seemed unused.", | |||
535 | } | 538 | } | |
536 | } | 539 | } |
@@ -801,52 +801,57 @@ func (mkline *MkLine) CheckText(text str | @@ -801,52 +801,57 @@ func (mkline *MkLine) CheckText(text str | |||
801 | varbase, varext := m[1], m[2] | 801 | varbase, varext := m[1], m[2] | |
802 | varname := varbase + varext | 802 | varname := varbase + varext | |
803 | varcanon := varnameCanon(varname) | 803 | varcanon := varnameCanon(varname) | |
804 | instead := G.globalData.Deprecated[varname] | 804 | instead := G.globalData.Deprecated[varname] | |
805 | if instead == "" { | 805 | if instead == "" { | |
806 | instead = G.globalData.Deprecated[varcanon] | 806 | instead = G.globalData.Deprecated[varcanon] | |
807 | } | 807 | } | |
808 | if instead != "" { | 808 | if instead != "" { | |
809 | mkline.Warn2("Use of %q is deprecated. %s", varname, instead) | 809 | mkline.Warn2("Use of %q is deprecated. %s", varname, instead) | |
810 | } | 810 | } | |
811 | } | 811 | } | |
812 | } | 812 | } | |
813 | 813 | |||
814 | func (mkline *MkLine) CheckIf() { | 814 | func (mkline *MkLine) CheckCond() { | |
815 | if G.opts.DebugTrace { | 815 | if G.opts.DebugTrace { | |
816 | defer tracecall0()() | 816 | defer tracecall0()() | |
817 | } | 817 | } | |
818 | 818 | |||
819 | tree := mkline.parseMkCond(mkline.Args()) | 819 | p := NewParser(mkline.Args()) | |
820 | cond := p.MkCond() | |||
821 | if !p.EOF() { | |||
822 | mkline.Warn1("Invalid conditional %q.", mkline.Args()) | |||
823 | return | |||
824 | } | |||
820 | 825 | |||
821 | { | 826 | cond.Visit("empty", func(node *Tree) { | |
822 | var pvarname, ppattern *string | 827 | varuse := node.args[0].(MkVarUse) | |
823 | if tree.Match(NewTree("not", NewTree("empty", NewTree("match", &pvarname, &ppattern)))) { | 828 | varname := varuse.varname | |
824 | vartype := mkline.getVariableType(*pvarname) | 829 | for _, modifier := range varuse.modifiers { | |
825 | if vartype != nil && vartype.checker.IsEnum() { | 830 | if modifier[0] == 'M' || modifier[0] == 'N' { | |
826 | if !matches(*ppattern, `[\$\[*]`) && !vartype.checker.HasEnum(*ppattern) { | 831 | mkline.CheckVartype(varname, opUseMatch, modifier[1:], "") | |
827 | mkline.Warn2("Invalid :M value %q. Only { %s } are allowed.", *ppattern, vartype.checker.AllowedEnums()) | |||
828 | } | |||
829 | } | 832 | } | |
830 | return | |||
831 | } | 833 | } | |
832 | } | 834 | }) | |
833 | 835 | |||
834 | { | 836 | cond.Visit("compareVarStr", func(node *Tree) { | |
835 | var pop, pvarname, pvalue *string | 837 | varuse := node.args[0].(MkVarUse) | |
836 | if tree.Match(NewTree("compareVarStr", &pvarname, &pop, &pvalue)) { | 838 | varname := varuse.varname | |
837 | mkline.CheckVartype(*pvarname, opUse, *pvalue, "") | 839 | varmods := varuse.modifiers | |
840 | value := node.args[2].(string) | |||
841 | if len(varmods) == 0 || len(varmods) == 1 && matches(varmods[0], `^[MN]`) && value != "" { | |||
842 | mkline.CheckVartype(varname, opUseMatch, value, "") | |||
838 | } | 843 | } | |
839 | } | 844 | }) | |
840 | } | 845 | } | |
841 | 846 | |||
842 | func (mkline *MkLine) CheckValidCharactersInValue(reValid string) { | 847 | func (mkline *MkLine) CheckValidCharactersInValue(reValid string) { | |
843 | rest := regcomp(reValid).ReplaceAllString(mkline.Value(), "") | 848 | rest := regcomp(reValid).ReplaceAllString(mkline.Value(), "") | |
844 | if rest != "" { | 849 | if rest != "" { | |
845 | uni := "" | 850 | uni := "" | |
846 | for _, c := range rest { | 851 | for _, c := range rest { | |
847 | uni += fmt.Sprintf(" %U", c) | 852 | uni += fmt.Sprintf(" %U", c) | |
848 | } | 853 | } | |
849 | mkline.Warn2("%s contains invalid characters (%s).", mkline.Varname(), uni[1:]) | 854 | mkline.Warn2("%s contains invalid characters (%s).", mkline.Varname(), uni[1:]) | |
850 | } | 855 | } | |
851 | } | 856 | } | |
852 | 857 | |||
@@ -940,57 +945,26 @@ func matchMkCond(text string) (m bool, i | @@ -940,57 +945,26 @@ func matchMkCond(text string) (m bool, i | |||
940 | i++ | 945 | i++ | |
941 | } | 946 | } | |
942 | for i > argsStart && (text[i-1] == ' ' || text[i-1] == '\t') { | 947 | for i > argsStart && (text[i-1] == ' ' || text[i-1] == '\t') { | |
943 | i-- | 948 | i-- | |
944 | } | 949 | } | |
945 | argsEnd := i | 950 | argsEnd := i | |
946 | 951 | |||
947 | m = true | 952 | m = true | |
948 | indent = text[indentStart:indentEnd] | 953 | indent = text[indentStart:indentEnd] | |
949 | args = text[argsStart:argsEnd] | 954 | args = text[argsStart:argsEnd] | |
950 | return | 955 | return | |
951 | } | 956 | } | |
952 | 957 | |||
953 | func (mkline *MkLine) parseMkCond(cond string) *Tree { | |||
954 | if G.opts.DebugTrace { | |||
955 | defer tracecall1(cond)() | |||
956 | } | |||
957 | ||||
958 | const ( | |||
959 | repartVarname = `[A-Z_][A-Z0-9_]*(?:\.[\w_+\-]+)?` | |||
960 | reDefined = `^defined\((` + repartVarname + `)\)` | |||
961 | reEmpty = `^empty\((` + repartVarname + `)\)` | |||
962 | reEmptyMatch = `^empty\((` + repartVarname + `):M([^\$:{})]+)\)` | |||
963 | reCompare = `^\$\{(` + repartVarname + `)\}\s+(==|!=)\s+"([^"\$\\]*)"` | |||
964 | ) | |||
965 | ||||
966 | if m, rest := replaceFirst(cond, `^!`, ""); m != nil { | |||
967 | return NewTree("not", mkline.parseMkCond(rest)) | |||
968 | } | |||
969 | if m, rest := replaceFirst(cond, reDefined, ""); m != nil { | |||
970 | return NewTree("defined", mkline.parseMkCond(rest)) | |||
971 | } | |||
972 | if m, _ := replaceFirst(cond, reEmpty, ""); m != nil { | |||
973 | return NewTree("empty", m[1]) | |||
974 | } | |||
975 | if m, _ := replaceFirst(cond, reEmptyMatch, ""); m != nil { | |||
976 | return NewTree("empty", NewTree("match", m[1], m[2])) | |||
977 | } | |||
978 | if m, _ := replaceFirst(cond, reCompare, ""); m != nil { | |||
979 | return NewTree("compareVarStr", m[1], m[2], m[3]) | |||
980 | } | |||
981 | return NewTree("unknown", cond) | |||
982 | } | |||
983 | ||||
984 | type NeedsQuoting uint8 | 958 | type NeedsQuoting uint8 | |
985 | 959 | |||
986 | const ( | 960 | const ( | |
987 | nqNo NeedsQuoting = iota | 961 | nqNo NeedsQuoting = iota | |
988 | nqYes | 962 | nqYes | |
989 | nqDoesntMatter | 963 | nqDoesntMatter | |
990 | nqDontKnow | 964 | nqDontKnow | |
991 | ) | 965 | ) | |
992 | 966 | |||
993 | func (mkline *MkLine) variableNeedsQuoting(varname string, vuc *VarUseContext) (needsQuoting NeedsQuoting) { | 967 | func (mkline *MkLine) variableNeedsQuoting(varname string, vuc *VarUseContext) (needsQuoting NeedsQuoting) { | |
994 | if G.opts.DebugTrace { | 968 | if G.opts.DebugTrace { | |
995 | defer tracecall(varname, *vuc, "=>", needsQuoting)() | 969 | defer tracecall(varname, *vuc, "=>", needsQuoting)() | |
996 | } | 970 | } |
@@ -295,59 +295,51 @@ func (s *Suite) TestMkLine_fields(c *che | @@ -295,59 +295,51 @@ func (s *Suite) TestMkLine_fields(c *che | |||
295 | c.Check(s.Output(), equals, "WARN: test.mk:9: Space before colon in dependency line.\n") | 295 | c.Check(s.Output(), equals, "WARN: test.mk:9: Space before colon in dependency line.\n") | |
296 | } | 296 | } | |
297 | 297 | |||
298 | func (s *Suite) TestMkLine_checkVarassign(c *check.C) { | 298 | func (s *Suite) TestMkLine_checkVarassign(c *check.C) { | |
299 | G.Pkg = NewPackage("graphics/gimp-fix-ca") | 299 | G.Pkg = NewPackage("graphics/gimp-fix-ca") | |
300 | G.globalData.InitVartypes() | 300 | G.globalData.InitVartypes() | |
301 | mkline := NewMkLine(NewLine("fname", 10, "MASTER_SITES=http://registry.gimp.org/file/fix-ca.c?action=download&id=9884&file=", nil)) | 301 | mkline := NewMkLine(NewLine("fname", 10, "MASTER_SITES=http://registry.gimp.org/file/fix-ca.c?action=download&id=9884&file=", nil)) | |
302 | 302 | |||
303 | mkline.CheckVarassign() | 303 | mkline.CheckVarassign() | |
304 | 304 | |||
305 | c.Check(s.Output(), equals, "") | 305 | c.Check(s.Output(), equals, "") | |
306 | } | 306 | } | |
307 | 307 | |||
308 | func (s *Suite) TestParseMkCond_NotEmptyMatch(c *check.C) { | |||
309 | mkline := NewMkLine(NewLine("fname", 1, ".if !empty(USE_LIBTOOL:M[Yy][Ee][Ss])", nil)) | |||
310 | ||||
311 | cond := mkline.parseMkCond(mkline.Args()) | |||
312 | ||||
313 | c.Check(cond, check.DeepEquals, NewTree("not", NewTree("empty", NewTree("match", "USE_LIBTOOL", "[Yy][Ee][Ss]")))) | |||
314 | } | |||
315 | ||||
316 | func (s *Suite) TestParseMkCond_Compare(c *check.C) { | |||
317 | mkline := NewMkLine(NewLine("fname", 1, ".if ${VARNAME} != \"Value\"", nil)) | |||
318 | ||||
319 | cond := mkline.parseMkCond(mkline.Args()) | |||
320 | ||||
321 | c.Check(cond, check.DeepEquals, NewTree("compareVarStr", "VARNAME", "!=", "Value")) | |||
322 | } | |||
323 | ||||
324 | func (s *Suite) TestChecklineMkCondition(c *check.C) { | 308 | func (s *Suite) TestChecklineMkCondition(c *check.C) { | |
325 | s.UseCommandLine(c, "-Wtypes") | 309 | s.UseCommandLine(c, "-Wtypes") | |
326 | G.globalData.InitVartypes() | 310 | G.globalData.InitVartypes() | |
327 | 311 | |||
328 | NewMkLine(NewLine("fname", 1, ".if !empty(PKGSRC_COMPILER:Mmycc)", nil)).CheckIf() | 312 | NewMkLine(NewLine("fname", 1, ".if !empty(PKGSRC_COMPILER:Mmycc)", nil)).CheckCond() | |
329 | 313 | |||
330 | c.Check(s.Stdout(), equals, "WARN: fname:1: Invalid :M value \"mycc\". "+ | 314 | c.Check(s.Stdout(), equals, "WARN: fname:1: The pattern \"mycc\" cannot match any of "+ | |
331 | "Only { ccache ccc clang distcc f2c gcc hp icc ido gcc mipspro "+ | 315 | "{ ccache ccc clang distcc f2c gcc hp icc ido "+ | |
332 | "mipspro-ucode pcc sunpro xlc } are allowed.\n") | 316 | "gcc mipspro mipspro-ucode pcc sunpro xlc } for PKGSRC_COMPILER.\n") | |
333 | 317 | |||
334 | NewMkLine(NewLine("fname", 1, ".elif ${A} != ${B}", nil)).CheckIf() | 318 | NewMkLine(NewLine("fname", 1, ".elif ${A} != ${B}", nil)).CheckCond() | |
335 | 319 | |||
336 | c.Check(s.Stdout(), equals, "") // Unknown condition types are silently ignored | 320 | c.Check(s.Stdout(), equals, "") | |
337 | 321 | |||
338 | NewMkLine(NewLine("fname", 1, ".if ${HOMEPAGE} == \"mailto:someone@example.org\"", nil)).CheckIf() | 322 | NewMkLine(NewLine("fname", 1, ".if ${HOMEPAGE} == \"mailto:someone@example.org\"", nil)).CheckCond() | |
339 | 323 | |||
340 | c.Check(s.Output(), equals, "WARN: fname:1: \"mailto:someone@example.org\" is not a valid URL.\n") | 324 | c.Check(s.Output(), equals, "WARN: fname:1: \"mailto:someone@example.org\" is not a valid URL.\n") | |
325 | ||||
326 | NewMkLine(NewLine("fname", 1, ".if !empty(PKGSRC_RUN_TEST:M[Y][eE][sS])", nil)).CheckCond() | |||
327 | ||||
328 | c.Check(s.Output(), equals, "WARN: fname:1: PKGSRC_RUN_TEST should be matched against \"[yY][eE][sS]\" or \"[nN][oO]\", not \"[Y][eE][sS]\".\n") | |||
329 | ||||
330 | NewMkLine(NewLine("fname", 1, ".if !empty(IS_BUILTIN.Xfixes:M[yY][eE][sS])", nil)).CheckCond() | |||
331 | ||||
332 | c.Check(s.Output(), equals, "") | |||
341 | } | 333 | } | |
342 | 334 | |||
343 | func (s *Suite) TestMkLine_variableNeedsQuoting(c *check.C) { | 335 | func (s *Suite) TestMkLine_variableNeedsQuoting(c *check.C) { | |
344 | mkline := NewMkLine(NewLine("fname", 1, "PKGNAME := ${UNKNOWN}", nil)) | 336 | mkline := NewMkLine(NewLine("fname", 1, "PKGNAME := ${UNKNOWN}", nil)) | |
345 | G.globalData.InitVartypes() | 337 | G.globalData.InitVartypes() | |
346 | pkgnameType := G.globalData.vartypes["PKGNAME"] | 338 | pkgnameType := G.globalData.vartypes["PKGNAME"] | |
347 | 339 | |||
348 | vuc := &VarUseContext{pkgnameType, vucTimeParse, vucQuotUnknown, vucExtentUnknown} | 340 | vuc := &VarUseContext{pkgnameType, vucTimeParse, vucQuotUnknown, vucExtentUnknown} | |
349 | nq := mkline.variableNeedsQuoting("UNKNOWN", vuc) | 341 | nq := mkline.variableNeedsQuoting("UNKNOWN", vuc) | |
350 | 342 | |||
351 | c.Check(nq, equals, nqDontKnow) | 343 | c.Check(nq, equals, nqDontKnow) | |
352 | } | 344 | } | |
353 | 345 | |||
@@ -433,27 +425,27 @@ func (s *Suite) TestMkLine_CheckVarusePe | @@ -433,27 +425,27 @@ func (s *Suite) TestMkLine_CheckVarusePe | |||
433 | "COMMENT=\t${GAMES_USER}", | 425 | "COMMENT=\t${GAMES_USER}", | |
434 | "COMMENT:=\t${PKGBASE}", | 426 | "COMMENT:=\t${PKGBASE}", | |
435 | "PYPKGPREFIX=${PKGBASE}") | 427 | "PYPKGPREFIX=${PKGBASE}") | |
436 | G.globalData.UserDefinedVars = map[string]*MkLine{ | 428 | G.globalData.UserDefinedVars = map[string]*MkLine{ | |
437 | "GAMES_USER": mklines.mklines[0], | 429 | "GAMES_USER": mklines.mklines[0], | |
438 | } | 430 | } | |
439 | 431 | |||
440 | mklines.Check() | 432 | mklines.Check() | |
441 | 433 | |||
442 | c.Check(s.Output(), equals, ""+ | 434 | c.Check(s.Output(), equals, ""+ | |
443 | "WARN: options.mk:2: The user-defined variable GAMES_USER is used but not added to BUILD_DEFS.\n"+ | 435 | "WARN: options.mk:2: The user-defined variable GAMES_USER is used but not added to BUILD_DEFS.\n"+ | |
444 | "WARN: options.mk:3: PKGBASE should not be evaluated at load time.\n"+ | 436 | "WARN: options.mk:3: PKGBASE should not be evaluated at load time.\n"+ | |
445 | "WARN: options.mk:4: The variable PYPKGPREFIX may not be set in this file; it would be ok in pyversion.mk.\n"+ | 437 | "WARN: options.mk:4: The variable PYPKGPREFIX may not be set in this file; it would be ok in pyversion.mk.\n"+ | |
446 | "WARN: options.mk:4: \"${PKGBASE}\" is not valid for PYPKGPREFIX. Use one of { py27 py33 py34 } instead.\n"+ | 438 | "WARN: options.mk:4: \"${PKGBASE}\" is not valid for PYPKGPREFIX. Use one of { py27 py33 py34 py35 } instead.\n"+ | |
447 | "WARN: options.mk:4: PKGBASE should not be evaluated indirectly at load time.\n"+ | 439 | "WARN: options.mk:4: PKGBASE should not be evaluated indirectly at load time.\n"+ | |
448 | "NOTE: options.mk:4: This variable value should be aligned to column 17.\n") | 440 | "NOTE: options.mk:4: This variable value should be aligned to column 17.\n") | |
449 | } | 441 | } | |
450 | 442 | |||
451 | func (s *Suite) TestMkLine_WarnVaruseLocalbase(c *check.C) { | 443 | func (s *Suite) TestMkLine_WarnVaruseLocalbase(c *check.C) { | |
452 | mkline := NewMkLine(NewLine("options.mk", 56, "PKGNAME=${LOCALBASE}", nil)) | 444 | mkline := NewMkLine(NewLine("options.mk", 56, "PKGNAME=${LOCALBASE}", nil)) | |
453 | 445 | |||
454 | mkline.WarnVaruseLocalbase() | 446 | mkline.WarnVaruseLocalbase() | |
455 | 447 | |||
456 | c.Check(s.Output(), equals, "WARN: options.mk:56: The LOCALBASE variable should not be used by packages.\n") | 448 | c.Check(s.Output(), equals, "WARN: options.mk:56: The LOCALBASE variable should not be used by packages.\n") | |
457 | } | 449 | } | |
458 | 450 | |||
459 | func (s *Suite) TestMkLine_Misc(c *check.C) { | 451 | func (s *Suite) TestMkLine_Misc(c *check.C) { |
@@ -32,33 +32,33 @@ func NewMkLines(lines []*Line) *MkLines | @@ -32,33 +32,33 @@ func NewMkLines(lines []*Line) *MkLines | |||
32 | return &MkLines{ | 32 | return &MkLines{ | |
33 | mklines, | 33 | mklines, | |
34 | lines, | 34 | lines, | |
35 | make(map[string]bool), | 35 | make(map[string]bool), | |
36 | make([]int, 1), | 36 | make([]int, 1), | |
37 | "", | 37 | "", | |
38 | make(map[string]*MkLine), | 38 | make(map[string]*MkLine), | |
39 | make(map[string]*MkLine), | 39 | make(map[string]*MkLine), | |
40 | make(map[string]bool), | 40 | make(map[string]bool), | |
41 | make(map[string]bool), | 41 | make(map[string]bool), | |
42 | tools} | 42 | tools} | |
43 | } | 43 | } | |
44 | 44 | |||
45 | func (mklines *MkLines) IndentDepth() int { | 45 | func (mklines *MkLines) indentDepth() int { | |
46 | return mklines.indentation[len(mklines.indentation)-1] | 46 | return mklines.indentation[len(mklines.indentation)-1] | |
47 | } | 47 | } | |
48 | func (mklines *MkLines) PopIndent() { | 48 | func (mklines *MkLines) popIndent() { | |
49 | mklines.indentation = mklines.indentation[:len(mklines.indentation)-1] | 49 | mklines.indentation = mklines.indentation[:len(mklines.indentation)-1] | |
50 | } | 50 | } | |
51 | func (mklines *MkLines) PushIndent(indent int) { | 51 | func (mklines *MkLines) pushIndent(indent int) { | |
52 | mklines.indentation = append(mklines.indentation, indent) | 52 | mklines.indentation = append(mklines.indentation, indent) | |
53 | } | 53 | } | |
54 | 54 | |||
55 | func (mklines *MkLines) DefineVar(mkline *MkLine, varname string) { | 55 | func (mklines *MkLines) DefineVar(mkline *MkLine, varname string) { | |
56 | if mklines.vardef[varname] == nil { | 56 | if mklines.vardef[varname] == nil { | |
57 | mklines.vardef[varname] = mkline | 57 | mklines.vardef[varname] = mkline | |
58 | } | 58 | } | |
59 | varcanon := varnameCanon(varname) | 59 | varcanon := varnameCanon(varname) | |
60 | if mklines.vardef[varcanon] == nil { | 60 | if mklines.vardef[varcanon] == nil { | |
61 | mklines.vardef[varcanon] = mkline | 61 | mklines.vardef[varcanon] = mkline | |
62 | } | 62 | } | |
63 | } | 63 | } | |
64 | 64 | |||
@@ -78,45 +78,45 @@ func (mklines *MkLines) VarValue(varname | @@ -78,45 +78,45 @@ func (mklines *MkLines) VarValue(varname | |||
78 | } | 78 | } | |
79 | return "", false | 79 | return "", false | |
80 | } | 80 | } | |
81 | 81 | |||
82 | func (mklines *MkLines) Check() { | 82 | func (mklines *MkLines) Check() { | |
83 | if G.opts.DebugTrace { | 83 | if G.opts.DebugTrace { | |
84 | defer tracecall1(mklines.lines[0].Fname)() | 84 | defer tracecall1(mklines.lines[0].Fname)() | |
85 | } | 85 | } | |
86 | 86 | |||
87 | G.Mk = mklines | 87 | G.Mk = mklines | |
88 | defer func() { G.Mk = nil }() | 88 | defer func() { G.Mk = nil }() | |
89 | 89 | |||
90 | allowedTargets := make(map[string]bool) | 90 | allowedTargets := make(map[string]bool) | |
91 | prefixes := splitOnSpace("pre do post") | 91 | prefixes := [...]string{"pre", "do", "post"} | |
92 | actions := splitOnSpace("fetch extract patch tools wrapper configure build test install package clean") | 92 | actions := [...]string{"fetch", "extract", "patch", "tools", "wrapper", "configure", "build", "test", "install", "package", "clean"} | |
93 | for _, prefix := range prefixes { | 93 | for _, prefix := range prefixes { | |
94 | for _, action := range actions { | 94 | for _, action := range actions { | |
95 | allowedTargets[prefix+"-"+action] = true | 95 | allowedTargets[prefix+"-"+action] = true | |
96 | } | 96 | } | |
97 | } | 97 | } | |
98 | 98 | |||
99 | // In the first pass, all additions to BUILD_DEFS and USE_TOOLS | 99 | // In the first pass, all additions to BUILD_DEFS and USE_TOOLS | |
100 | // are collected to make the order of the definitions irrelevant. | 100 | // are collected to make the order of the definitions irrelevant. | |
101 | mklines.DetermineUsedVariables() | 101 | mklines.DetermineUsedVariables() | |
102 | mklines.determineDefinedVariables() | 102 | mklines.determineDefinedVariables() | |
103 | 103 | |||
104 | // In the second pass, the actual checks are done. | 104 | // In the second pass, the actual checks are done. | |
105 | 105 | |||
106 | mklines.lines[0].CheckRcsid(`#\s+`, "# ") | 106 | mklines.lines[0].CheckRcsid(`#\s+`, "# ") | |
107 | 107 | |||
108 | substcontext := new(SubstContext) | 108 | var substcontext SubstContext | |
109 | varalign := new(VaralignBlock) | 109 | var varalign VaralignBlock | |
110 | for _, mkline := range mklines.mklines { | 110 | for _, mkline := range mklines.mklines { | |
111 | mkline.Line.CheckTrailingWhitespace() | 111 | mkline.Line.CheckTrailingWhitespace() | |
112 | mkline.Line.CheckValidCharacters(`[\t -~]`) | 112 | mkline.Line.CheckValidCharacters(`[\t -~]`) | |
113 | varalign.Check(mkline) | 113 | varalign.Check(mkline) | |
114 | 114 | |||
115 | switch { | 115 | switch { | |
116 | case mkline.IsEmpty(): | 116 | case mkline.IsEmpty(): | |
117 | substcontext.Finish(mkline) | 117 | substcontext.Finish(mkline) | |
118 | 118 | |||
119 | case mkline.IsVarassign(): | 119 | case mkline.IsVarassign(): | |
120 | mklines.target = "" | 120 | mklines.target = "" | |
121 | mkline.CheckVarassign() | 121 | mkline.CheckVarassign() | |
122 | substcontext.Varassign(mkline) | 122 | substcontext.Varassign(mkline) | |
@@ -133,28 +133,28 @@ func (mklines *MkLines) Check() { | @@ -133,28 +133,28 @@ func (mklines *MkLines) Check() { | |||
133 | case mkline.IsCond(): | 133 | case mkline.IsCond(): | |
134 | mklines.checklineCond(mkline) | 134 | mklines.checklineCond(mkline) | |
135 | 135 | |||
136 | case mkline.IsDependency(): | 136 | case mkline.IsDependency(): | |
137 | mklines.checklineDependencyRule(mkline, mkline.Targets(), mkline.Sources(), allowedTargets) | 137 | mklines.checklineDependencyRule(mkline, mkline.Targets(), mkline.Sources(), allowedTargets) | |
138 | } | 138 | } | |
139 | } | 139 | } | |
140 | lastMkline := mklines.mklines[len(mklines.mklines)-1] | 140 | lastMkline := mklines.mklines[len(mklines.mklines)-1] | |
141 | substcontext.Finish(lastMkline) | 141 | substcontext.Finish(lastMkline) | |
142 | varalign.Finish() | 142 | varalign.Finish() | |
143 | 143 | |||
144 | ChecklinesTrailingEmptyLines(mklines.lines) | 144 | ChecklinesTrailingEmptyLines(mklines.lines) | |
145 | 145 | |||
146 | if len(mklines.indentation) != 1 && mklines.IndentDepth() != 0 { | 146 | if len(mklines.indentation) != 1 && mklines.indentDepth() != 0 { | |
147 | lastMkline.Line.Errorf("Directive indentation is not 0, but %d.", mklines.IndentDepth()) | 147 | lastMkline.Line.Errorf("Directive indentation is not 0, but %d.", mklines.indentDepth()) | |
148 | } | 148 | } | |
149 | 149 | |||
150 | SaveAutofixChanges(mklines.lines) | 150 | SaveAutofixChanges(mklines.lines) | |
151 | } | 151 | } | |
152 | 152 | |||
153 | func (mklines *MkLines) determineDefinedVariables() { | 153 | func (mklines *MkLines) determineDefinedVariables() { | |
154 | for _, mkline := range mklines.mklines { | 154 | for _, mkline := range mklines.mklines { | |
155 | if !mkline.IsVarassign() { | 155 | if !mkline.IsVarassign() { | |
156 | continue | 156 | continue | |
157 | } | 157 | } | |
158 | 158 | |||
159 | varcanon := mkline.Varcanon() | 159 | varcanon := mkline.Varcanon() | |
160 | switch varcanon { | 160 | switch varcanon { | |
@@ -213,59 +213,59 @@ func (mklines *MkLines) DetermineUsedVar | @@ -213,59 +213,59 @@ func (mklines *MkLines) DetermineUsedVar | |||
213 | varnames := mkline.determineUsedVariables() | 213 | varnames := mkline.determineUsedVariables() | |
214 | for _, varname := range varnames { | 214 | for _, varname := range varnames { | |
215 | mklines.UseVar(mkline, varname) | 215 | mklines.UseVar(mkline, varname) | |
216 | } | 216 | } | |
217 | } | 217 | } | |
218 | } | 218 | } | |
219 | 219 | |||
220 | func (mklines *MkLines) checklineCond(mkline *MkLine) { | 220 | func (mklines *MkLines) checklineCond(mkline *MkLine) { | |
221 | indent, directive, args := mkline.Indent(), mkline.Directive(), mkline.Args() | 221 | indent, directive, args := mkline.Indent(), mkline.Directive(), mkline.Args() | |
222 | 222 | |||
223 | switch directive { | 223 | switch directive { | |
224 | case "endif", "endfor", "elif", "else": | 224 | case "endif", "endfor", "elif", "else": | |
225 | if len(mklines.indentation) > 1 { | 225 | if len(mklines.indentation) > 1 { | |
226 | mklines.PopIndent() | 226 | mklines.popIndent() | |
227 | } else { | 227 | } else { | |
228 | mkline.Error1("Unmatched .%s.", directive) | 228 | mkline.Error1("Unmatched .%s.", directive) | |
229 | } | 229 | } | |
230 | } | 230 | } | |
231 | 231 | |||
232 | // Check the indentation | 232 | // Check the indentation | |
233 | if expected := strings.Repeat(" ", mklines.IndentDepth()); indent != expected { | 233 | if expected := strings.Repeat(" ", mklines.indentDepth()); indent != expected { | |
234 | if G.opts.WarnSpace && !mkline.Line.AutofixReplace("."+indent, "."+expected) { | 234 | if G.opts.WarnSpace && !mkline.Line.AutofixReplace("."+indent, "."+expected) { | |
235 | mkline.Line.Notef("This directive should be indented by %d spaces.", mklines.IndentDepth()) | 235 | mkline.Line.Notef("This directive should be indented by %d spaces.", mklines.indentDepth()) | |
236 | } | 236 | } | |
237 | } | 237 | } | |
238 | 238 | |||
239 | if directive == "if" && matches(args, `^!defined\([\w]+_MK\)$`) { | 239 | if directive == "if" && matches(args, `^!defined\([\w]+_MK\)$`) { | |
240 | mklines.PushIndent(mklines.IndentDepth()) | 240 | mklines.pushIndent(mklines.indentDepth()) | |
241 | 241 | |||
242 | } else if matches(directive, `^(?:if|ifdef|ifndef|for|elif|else)$`) { | 242 | } else if matches(directive, `^(?:if|ifdef|ifndef|for|elif|else)$`) { | |
243 | mklines.PushIndent(mklines.IndentDepth() + 2) | 243 | mklines.pushIndent(mklines.indentDepth() + 2) | |
244 | } | 244 | } | |
245 | 245 | |||
246 | reDirectivesWithArgs := `^(?:if|ifdef|ifndef|elif|for|undef)$` | 246 | reDirectivesWithArgs := `^(?:if|ifdef|ifndef|elif|for|undef)$` | |
247 | if matches(directive, reDirectivesWithArgs) && args == "" { | 247 | if matches(directive, reDirectivesWithArgs) && args == "" { | |
248 | mkline.Error1("\".%s\" requires arguments.", directive) | 248 | mkline.Error1("\".%s\" requires arguments.", directive) | |
249 | 249 | |||
250 | } else if !matches(directive, reDirectivesWithArgs) && args != "" { | 250 | } else if !matches(directive, reDirectivesWithArgs) && args != "" { | |
251 | mkline.Error1("\".%s\" does not take arguments.", directive) | 251 | mkline.Error1("\".%s\" does not take arguments.", directive) | |
252 | 252 | |||
253 | if directive == "else" { | 253 | if directive == "else" { | |
254 | mkline.Note0("If you meant \"else if\", use \".elif\".") | 254 | mkline.Note0("If you meant \"else if\", use \".elif\".") | |
255 | } | 255 | } | |
256 | 256 | |||
257 | } else if directive == "if" || directive == "elif" { | 257 | } else if directive == "if" || directive == "elif" { | |
258 | mkline.CheckIf() | 258 | mkline.CheckCond() | |
259 | 259 | |||
260 | } else if directive == "ifdef" || directive == "ifndef" { | 260 | } else if directive == "ifdef" || directive == "ifndef" { | |
261 | if matches(args, `\s`) { | 261 | if matches(args, `\s`) { | |
262 | mkline.Error1("The \".%s\" directive can only handle _one_ argument.", directive) | 262 | mkline.Error1("The \".%s\" directive can only handle _one_ argument.", directive) | |
263 | } else { | 263 | } else { | |
264 | mkline.Line.Warnf("The \".%s\" directive is deprecated. Please use \".if %sdefined(%s)\" instead.", | 264 | mkline.Line.Warnf("The \".%s\" directive is deprecated. Please use \".if %sdefined(%s)\" instead.", | |
265 | directive, ifelseStr(directive == "ifdef", "", "!"), args) | 265 | directive, ifelseStr(directive == "ifdef", "", "!"), args) | |
266 | } | 266 | } | |
267 | 267 | |||
268 | } else if directive == "for" { | 268 | } else if directive == "for" { | |
269 | if m, vars, values := match2(args, `^(\S+(?:\s*\S+)*?)\s+in\s+(.*)$`); m { | 269 | if m, vars, values := match2(args, `^(\S+(?:\s*\S+)*?)\s+in\s+(.*)$`); m { | |
270 | for _, forvar := range splitOnSpace(vars) { | 270 | for _, forvar := range splitOnSpace(vars) { | |
271 | if !G.Infrastructure && hasPrefix(forvar, "_") { | 271 | if !G.Infrastructure && hasPrefix(forvar, "_") { |
@@ -673,27 +673,30 @@ func (pkg *Package) ChecklinesPackageMak | @@ -673,27 +673,30 @@ func (pkg *Package) ChecklinesPackageMak | |||
673 | 673 | |||
674 | default: | 674 | default: | |
675 | if vars[varindex].count != many { | 675 | if vars[varindex].count != many { | |
676 | below[vars[varindex].varname] = belowWhat | 676 | below[vars[varindex].varname] = belowWhat | |
677 | varindex++ | 677 | varindex++ | |
678 | } | 678 | } | |
679 | lineno++ | 679 | lineno++ | |
680 | } | 680 | } | |
681 | belowWhat = varcanon | 681 | belowWhat = varcanon | |
682 | 682 | |||
683 | default: | 683 | default: | |
684 | for varindex < len(vars) { | 684 | for varindex < len(vars) { | |
685 | if vars[varindex].count == once && !maySkipSection { | 685 | if vars[varindex].count == once && !maySkipSection { | |
686 | line.Warn1("%s should be set here.", vars[varindex].varname) | 686 | line.Warn1("The canonical position for the required variable %s is here.", vars[varindex].varname) | |
687 | Explain( | |||
688 | "See doc/Makefile-example or the pkgsrc guide, section", | |||
689 | "\"Package components\", subsection \"Makefile\" for more information.") | |||
687 | } | 690 | } | |
688 | below[vars[varindex].varname] = belowWhat | 691 | below[vars[varindex].varname] = belowWhat | |
689 | varindex++ | 692 | varindex++ | |
690 | } | 693 | } | |
691 | nextSection = true | 694 | nextSection = true | |
692 | if text == "" { | 695 | if text == "" { | |
693 | belowWhat = "the previous empty line" | 696 | belowWhat = "the previous empty line" | |
694 | lineno++ | 697 | lineno++ | |
695 | } | 698 | } | |
696 | } | 699 | } | |
697 | } | 700 | } | |
698 | } | 701 | } | |
699 | 702 |
@@ -29,28 +29,28 @@ func (s *Suite) TestChecklinesPackageMak | @@ -29,28 +29,28 @@ func (s *Suite) TestChecklinesPackageMak | |||
29 | "CATEGORIES=x11")) | 29 | "CATEGORIES=x11")) | |
30 | 30 | |||
31 | c.Check(s.Output(), equals, "") | 31 | c.Check(s.Output(), equals, "") | |
32 | 32 | |||
33 | pkg.ChecklinesPackageMakefileVarorder(s.NewMkLines("Makefile", | 33 | pkg.ChecklinesPackageMakefileVarorder(s.NewMkLines("Makefile", | |
34 | "# $"+"NetBSD$", | 34 | "# $"+"NetBSD$", | |
35 | "", | 35 | "", | |
36 | "DISTNAME=9term", | 36 | "DISTNAME=9term", | |
37 | "CATEGORIES=x11", | 37 | "CATEGORIES=x11", | |
38 | "", | 38 | "", | |
39 | ".include \"../../mk/bsd.pkg.mk\"")) | 39 | ".include \"../../mk/bsd.pkg.mk\"")) | |
40 | 40 | |||
41 | c.Check(s.Output(), equals, ""+ | 41 | c.Check(s.Output(), equals, ""+ | |
42 | "WARN: Makefile:6: COMMENT should be set here.\n"+ | 42 | "WARN: Makefile:6: The canonical position for the required variable COMMENT is here.\n"+ | |
43 | "WARN: Makefile:6: LICENSE should be set here.\n") | 43 | "WARN: Makefile:6: The canonical position for the required variable LICENSE is here.\n") | |
44 | } | 44 | } | |
45 | 45 | |||
46 | func (s *Suite) TestGetNbpart(c *check.C) { | 46 | func (s *Suite) TestGetNbpart(c *check.C) { | |
47 | pkg := NewPackage("category/pkgbase") | 47 | pkg := NewPackage("category/pkgbase") | |
48 | pkg.vardef["PKGREVISION"] = NewMkLine(NewLine("Makefile", 1, "PKGREVISION=14", nil)) | 48 | pkg.vardef["PKGREVISION"] = NewMkLine(NewLine("Makefile", 1, "PKGREVISION=14", nil)) | |
49 | 49 | |||
50 | c.Check(pkg.getNbpart(), equals, "nb14") | 50 | c.Check(pkg.getNbpart(), equals, "nb14") | |
51 | 51 | |||
52 | pkg.vardef["PKGREVISION"] = NewMkLine(NewLine("Makefile", 1, "PKGREVISION=asdf", nil)) | 52 | pkg.vardef["PKGREVISION"] = NewMkLine(NewLine("Makefile", 1, "PKGREVISION=asdf", nil)) | |
53 | 53 | |||
54 | c.Check(pkg.getNbpart(), equals, "") | 54 | c.Check(pkg.getNbpart(), equals, "") | |
55 | } | 55 | } | |
56 | 56 |
@@ -347,26 +347,29 @@ func (pr *PrefixReplacer) PeekByte() int | @@ -347,26 +347,29 @@ func (pr *PrefixReplacer) PeekByte() int | |||
347 | } | 347 | } | |
348 | return int(rest[0]) | 348 | return int(rest[0]) | |
349 | } | 349 | } | |
350 | 350 | |||
351 | func (pr *PrefixReplacer) Mark() string { | 351 | func (pr *PrefixReplacer) Mark() string { | |
352 | return pr.rest | 352 | return pr.rest | |
353 | } | 353 | } | |
354 | func (pr *PrefixReplacer) Reset(mark string) { | 354 | func (pr *PrefixReplacer) Reset(mark string) { | |
355 | pr.rest = mark | 355 | pr.rest = mark | |
356 | } | 356 | } | |
357 | func (pr *PrefixReplacer) Skip(n int) { | 357 | func (pr *PrefixReplacer) Skip(n int) { | |
358 | pr.rest = pr.rest[n:] | 358 | pr.rest = pr.rest[n:] | |
359 | } | 359 | } | |
360 | func (pr *PrefixReplacer) SkipSpace() { | |||
361 | pr.rest = strings.TrimLeft(pr.rest, " \t") | |||
362 | } | |||
360 | func (pr *PrefixReplacer) Since(mark string) string { | 363 | func (pr *PrefixReplacer) Since(mark string) string { | |
361 | return mark[:len(mark)-len(pr.rest)] | 364 | return mark[:len(mark)-len(pr.rest)] | |
362 | } | 365 | } | |
363 | func (pr *PrefixReplacer) AdvanceRest() string { | 366 | func (pr *PrefixReplacer) AdvanceRest() string { | |
364 | rest := pr.rest | 367 | rest := pr.rest | |
365 | pr.rest = "" | 368 | pr.rest = "" | |
366 | return rest | 369 | return rest | |
367 | } | 370 | } | |
368 | 371 | |||
369 | func (pr *PrefixReplacer) verifyBits(bits0x00, bits0x20, bits0x40, bits0x60 uint32, re string) { | 372 | func (pr *PrefixReplacer) verifyBits(bits0x00, bits0x20, bits0x40, bits0x60 uint32, re string) { | |
370 | if G.TestingData.VerifiedBits[re] { | 373 | if G.TestingData.VerifiedBits[re] { | |
371 | return | 374 | return | |
372 | } | 375 | } |
@@ -270,13 +270,146 @@ func (p *Parser) VarUseModifiers(closing | @@ -270,13 +270,146 @@ func (p *Parser) VarUseModifiers(closing | |||
270 | } | 270 | } | |
271 | } | 271 | } | |
272 | 272 | |||
273 | repl.Reset(modifierMark) | 273 | repl.Reset(modifierMark) | |
274 | for p.VarUse() != nil || repl.AdvanceRegexp(`^([^:$`+closing+`]|\$\$)+`) { | 274 | for p.VarUse() != nil || repl.AdvanceRegexp(`^([^:$`+closing+`]|\$\$)+`) { | |
275 | } | 275 | } | |
276 | if suffixSubst := repl.Since(modifierMark); contains(suffixSubst, "=") { | 276 | if suffixSubst := repl.Since(modifierMark); contains(suffixSubst, "=") { | |
277 | modifiers = append(modifiers, suffixSubst) | 277 | modifiers = append(modifiers, suffixSubst) | |
278 | continue | 278 | continue | |
279 | } | 279 | } | |
280 | } | 280 | } | |
281 | return modifiers | 281 | return modifiers | |
282 | } | 282 | } | |
283 | ||||
284 | func (p *Parser) MkCond() *Tree { | |||
285 | return p.mkCondOr() | |||
286 | } | |||
287 | ||||
288 | func (p *Parser) mkCondOr() *Tree { | |||
289 | and := p.mkCondAnd() | |||
290 | if and == nil { | |||
291 | return nil | |||
292 | } | |||
293 | ||||
294 | ands := append([]interface{}(nil), and) | |||
295 | for { | |||
296 | mark := p.repl.Mark() | |||
297 | if !p.repl.AdvanceRegexp(`^\s*\|\|\s*`) { | |||
298 | break | |||
299 | } | |||
300 | next := p.mkCondAnd() | |||
301 | if next == nil { | |||
302 | p.repl.Reset(mark) | |||
303 | break | |||
304 | } | |||
305 | ands = append(ands, next) | |||
306 | } | |||
307 | if len(ands) == 1 { | |||
308 | return and | |||
309 | } | |||
310 | return NewTree("or", ands...) | |||
311 | } | |||
312 | ||||
313 | func (p *Parser) mkCondAnd() *Tree { | |||
314 | atom := p.mkCondAtom() | |||
315 | if atom == nil { | |||
316 | return nil | |||
317 | } | |||
318 | ||||
319 | atoms := append([]interface{}(nil), atom) | |||
320 | for { | |||
321 | mark := p.repl.Mark() | |||
322 | if !p.repl.AdvanceRegexp(`^\s*&&\s*`) { | |||
323 | break | |||
324 | } | |||
325 | next := p.mkCondAtom() | |||
326 | if next == nil { | |||
327 | p.repl.Reset(mark) | |||
328 | break | |||
329 | } | |||
330 | atoms = append(atoms, next) | |||
331 | } | |||
332 | if len(atoms) == 1 { | |||
333 | return atom | |||
334 | } | |||
335 | return NewTree("and", atoms...) | |||
336 | } | |||
337 | ||||
338 | func (p *Parser) mkCondAtom() *Tree { | |||
339 | if G.opts.DebugTrace { | |||
340 | defer tracecall1(p.Rest())() | |||
341 | } | |||
342 | ||||
343 | repl := p.repl | |||
344 | mark := repl.Mark() | |||
345 | repl.SkipSpace() | |||
346 | switch { | |||
347 | case repl.AdvanceStr("!"): | |||
348 | cond := p.mkCondAtom() | |||
349 | if cond != nil { | |||
350 | return NewTree("not", cond) | |||
351 | } | |||
352 | case repl.AdvanceStr("("): | |||
353 | cond := p.MkCond() | |||
354 | if cond != nil { | |||
355 | repl.SkipSpace() | |||
356 | if repl.AdvanceStr(")") { | |||
357 | return cond | |||
358 | } | |||
359 | } | |||
360 | case repl.AdvanceRegexp(`^defined\s*\(`): | |||
361 | if varname := p.Varname(); varname != "" { | |||
362 | if repl.AdvanceStr(")") { | |||
363 | return NewTree("defined", varname) | |||
364 | } | |||
365 | } | |||
366 | case repl.AdvanceRegexp(`^empty\s*\(`): | |||
367 | if varname := p.Varname(); varname != "" { | |||
368 | modifiers := p.VarUseModifiers(")") | |||
369 | if repl.AdvanceStr(")") { | |||
370 | return NewTree("empty", MkVarUse{varname, modifiers}) | |||
371 | } | |||
372 | } | |||
373 | case repl.AdvanceRegexp(`^(commands|exists|make|target)\s*\(`): | |||
374 | funcname := repl.m[1] | |||
375 | argMark := repl.Mark() | |||
376 | for p.VarUse() != nil || repl.AdvanceRegexp(`^[^$)]+`) { | |||
377 | } | |||
378 | arg := repl.Since(argMark) | |||
379 | if repl.AdvanceStr(")") { | |||
380 | return NewTree(funcname, arg) | |||
381 | } | |||
382 | default: | |||
383 | lhs := p.VarUse() | |||
384 | mark := repl.Mark() | |||
385 | if lhs == nil && repl.AdvanceStr("\"") { | |||
386 | if quotedLhs := p.VarUse(); quotedLhs != nil && repl.AdvanceStr("\"") { | |||
387 | lhs = quotedLhs | |||
388 | } else { | |||
389 | repl.Reset(mark) | |||
390 | } | |||
391 | } | |||
392 | if lhs != nil { | |||
393 | if repl.AdvanceRegexp(`^\s*(<|<=|==|!=|>=|>)\s*(\d+(?:\.\d+)?)`) { | |||
394 | return NewTree("compareVarNum", *lhs, repl.m[1], repl.m[2]) | |||
395 | } | |||
396 | if repl.AdvanceRegexp(`^\s*(<|<=|==|!=|>=|>)\s*`) { | |||
397 | op := repl.m[1] | |||
398 | if (op == "!=" || op == "==") && repl.AdvanceRegexp(`^"([^"\$\\]*)"`) { | |||
399 | return NewTree("compareVarStr", *lhs, op, repl.m[1]) | |||
400 | } else if repl.AdvanceRegexp(`^\w+`) { | |||
401 | return NewTree("compareVarStr", *lhs, op, repl.m[0]) | |||
402 | } else if rhs := p.VarUse(); rhs != nil { | |||
403 | return NewTree("compareVarVar", *lhs, op, *rhs) | |||
404 | } | |||
405 | } else { | |||
406 | return NewTree("not", NewTree("empty", *lhs)) // See devel/bmake/files/cond.c:/\* For \.if \$/ | |||
407 | } | |||
408 | } | |||
409 | if repl.AdvanceRegexp(`^\d+(?:\.\d+)?`) { | |||
410 | return NewTree("literalNum", repl.m[0]) | |||
411 | } | |||
412 | } | |||
413 | repl.Reset(mark) | |||
414 | return nil | |||
415 | } |
@@ -128,13 +128,90 @@ func (s *Suite) TestParser_MkTokens(c *c | @@ -128,13 +128,90 @@ func (s *Suite) TestParser_MkTokens(c *c | |||
128 | token("${${XKBBASE}/xkbcomp:L:Q}", varuse("${XKBBASE}/xkbcomp", "L", "Q")) | 128 | token("${${XKBBASE}/xkbcomp:L:Q}", varuse("${XKBBASE}/xkbcomp", "L", "Q")) | |
129 | token("${${PKGBASE} ${PKGVERSION}:L}", varuse("${PKGBASE} ${PKGVERSION}", "L")) | 129 | token("${${PKGBASE} ${PKGVERSION}:L}", varuse("${PKGBASE} ${PKGVERSION}", "L")) | |
130 | token("${empty(CFLAGS):?:-cflags ${CFLAGS:Q}}", varuse("empty(CFLAGS)", "?:-cflags ${CFLAGS:Q}")) | 130 | token("${empty(CFLAGS):?:-cflags ${CFLAGS:Q}}", varuse("empty(CFLAGS)", "?:-cflags ${CFLAGS:Q}")) | |
131 | token("${${${PKG_INFO} -E ${d} || echo:L:sh}:L:C/[^[0-9]]*/ /g:[1..3]:ts.}", | 131 | token("${${${PKG_INFO} -E ${d} || echo:L:sh}:L:C/[^[0-9]]*/ /g:[1..3]:ts.}", | |
132 | varuse("${${PKG_INFO} -E ${d} || echo:L:sh}", "L", "C/[^[0-9]]*/ /g", "[1..3]", "ts.")) | 132 | varuse("${${PKG_INFO} -E ${d} || echo:L:sh}", "L", "C/[^[0-9]]*/ /g", "[1..3]", "ts.")) | |
133 | token("${VAR:S/-//S/.//}", varuse("VAR", "S/-//", "S/.//")) // For :S and :C, the colon can be left out. | 133 | token("${VAR:S/-//S/.//}", varuse("VAR", "S/-//", "S/.//")) // For :S and :C, the colon can be left out. | |
134 | token("${VAR:ts}", varuse("VAR", "ts")) // The separator character can be left out. | 134 | token("${VAR:ts}", varuse("VAR", "ts")) // The separator character can be left out. | |
135 | token("${VAR:ts\\000012}", varuse("VAR", "ts\\000012")) // The separator character can be a long octal number. | 135 | token("${VAR:ts\\000012}", varuse("VAR", "ts\\000012")) // The separator character can be a long octal number. | |
136 | token("${VAR:ts\\124}", varuse("VAR", "ts\\124")) // Or even decimal. | 136 | token("${VAR:ts\\124}", varuse("VAR", "ts\\124")) // Or even decimal. | |
137 | 137 | |||
138 | parse("${VAR)", nil, "${VAR)") // Opening brace, closing parenthesis | 138 | parse("${VAR)", nil, "${VAR)") // Opening brace, closing parenthesis | |
139 | parse("$(VAR}", nil, "$(VAR}") // Opening parenthesis, closing brace | 139 | parse("$(VAR}", nil, "$(VAR}") // Opening parenthesis, closing brace | |
140 | } | 140 | } | |
141 | ||||
142 | func (s *Suite) TestParser_MkCond_Basics(c *check.C) { | |||
143 | condrest := func(input string, expectedTree *Tree, expectedRest string) { | |||
144 | p := NewParser(input) | |||
145 | actualTree := p.MkCond() | |||
146 | c.Check(actualTree, deepEquals, expectedTree) | |||
147 | c.Check(p.Rest(), equals, expectedRest) | |||
148 | } | |||
149 | cond := func(input string, expectedTree *Tree) { | |||
150 | condrest(input, expectedTree, "") | |||
151 | } | |||
152 | literal := func(literal string) MkToken { | |||
153 | return MkToken{literal: literal} | |||
154 | } | |||
155 | varuse := func(varname string, modifiers ...string) MkVarUse { | |||
156 | return MkVarUse{varname: varname, modifiers: modifiers} | |||
157 | } | |||
158 | _, _ = literal, varuse | |||
159 | ||||
160 | cond("${OPSYS:MNetBSD}", | |||
161 | NewTree("not", NewTree("empty", varuse("OPSYS", "MNetBSD")))) | |||
162 | cond("defined(VARNAME)", | |||
163 | NewTree("defined", "VARNAME")) | |||
164 | cond("empty(VARNAME)", | |||
165 | NewTree("empty", varuse("VARNAME"))) | |||
166 | cond("!empty(VARNAME)", | |||
167 | NewTree("not", NewTree("empty", varuse("VARNAME")))) | |||
168 | cond("!empty(VARNAME:M[yY][eE][sS])", | |||
169 | NewTree("not", NewTree("empty", varuse("VARNAME", "M[yY][eE][sS]")))) | |||
170 | cond("${VARNAME} != \"Value\"", | |||
171 | NewTree("compareVarStr", varuse("VARNAME"), "!=", "Value")) | |||
172 | cond("${VARNAME:Mi386} != \"Value\"", | |||
173 | NewTree("compareVarStr", varuse("VARNAME", "Mi386"), "!=", "Value")) | |||
174 | cond("${VARNAME} != Value", | |||
175 | NewTree("compareVarStr", varuse("VARNAME"), "!=", "Value")) | |||
176 | cond("\"${VARNAME}\" != Value", | |||
177 | NewTree("compareVarStr", varuse("VARNAME"), "!=", "Value")) | |||
178 | cond("(defined(VARNAME))", | |||
179 | NewTree("defined", "VARNAME")) | |||
180 | cond("exists(/etc/hosts)", | |||
181 | NewTree("exists", "/etc/hosts")) | |||
182 | cond("exists(${PREFIX}/var)", | |||
183 | NewTree("exists", "${PREFIX}/var")) | |||
184 | cond("${OPSYS} == \"NetBSD\" || ${OPSYS} == \"OpenBSD\"", | |||
185 | NewTree("or", | |||
186 | NewTree("compareVarStr", varuse("OPSYS"), "==", "NetBSD"), | |||
187 | NewTree("compareVarStr", varuse("OPSYS"), "==", "OpenBSD"))) | |||
188 | cond("${OPSYS} == \"NetBSD\" && ${MACHINE_ARCH} == \"i386\"", | |||
189 | NewTree("and", | |||
190 | NewTree("compareVarStr", varuse("OPSYS"), "==", "NetBSD"), | |||
191 | NewTree("compareVarStr", varuse("MACHINE_ARCH"), "==", "i386"))) | |||
192 | cond("defined(A) && defined(B) || defined(C) && defined(D)", | |||
193 | NewTree("or", | |||
194 | NewTree("and", | |||
195 | NewTree("defined", "A"), | |||
196 | NewTree("defined", "B")), | |||
197 | NewTree("and", | |||
198 | NewTree("defined", "C"), | |||
199 | NewTree("defined", "D")))) | |||
200 | ||||
201 | // Exotic cases | |||
202 | cond("0", | |||
203 | NewTree("literalNum", "0")) | |||
204 | cond("! ( defined(A) && empty(VARNAME) )", | |||
205 | NewTree("not", NewTree("and", NewTree("defined", "A"), NewTree("empty", varuse("VARNAME"))))) | |||
206 | cond("${REQD_MAJOR} > ${MAJOR}", | |||
207 | NewTree("compareVarVar", varuse("REQD_MAJOR"), ">", varuse("MAJOR"))) | |||
208 | cond("${OS_VERSION} >= 6.5", | |||
209 | NewTree("compareVarNum", varuse("OS_VERSION"), ">=", "6.5")) | |||
210 | cond("${OS_VERSION} == 5.3", | |||
211 | NewTree("compareVarNum", varuse("OS_VERSION"), "==", "5.3")) | |||
212 | ||||
213 | // Errors | |||
214 | condrest("!empty(PKG_OPTIONS:Msndfile) || defined(PKG_OPTIONS:Msamplerate)", | |||
215 | NewTree("not", NewTree("empty", varuse("PKG_OPTIONS", "Msndfile"))), | |||
216 | " || defined(PKG_OPTIONS:Msamplerate)") | |||
217 | } |
@@ -3,69 +3,39 @@ package main | @@ -3,69 +3,39 @@ package main | |||
3 | import ( | 3 | import ( | |
4 | "fmt" | 4 | "fmt" | |
5 | ) | 5 | ) | |
6 | 6 | |||
7 | type Tree struct { | 7 | type Tree struct { | |
8 | name string | 8 | name string | |
9 | args []interface{} | 9 | args []interface{} | |
10 | } | 10 | } | |
11 | 11 | |||
12 | func NewTree(name string, args ...interface{}) *Tree { | 12 | func NewTree(name string, args ...interface{}) *Tree { | |
13 | return &Tree{name, args} | 13 | return &Tree{name, args} | |
14 | } | 14 | } | |
15 | 15 | |||
16 | // Checks whether this tree matches the given pattern, and if so, | |||
17 | // copies the corresponding nodes from the tree to the pattern. | |||
18 | // If the match is partially successful, some or all of the variables | |||
19 | // may have been copied or not. | |||
20 | func (t *Tree) Match(pattern *Tree) bool { | |||
21 | if G.opts.DebugTrace { | |||
22 | defer tracecall(t, pattern)() | |||
23 | } | |||
24 | if t.name != pattern.name || len(t.args) != len(pattern.args) { | |||
25 | return false | |||
26 | } | |||
27 | ||||
28 | for i, targ := range t.args { | |||
29 | parg := pattern.args[i] | |||
30 | switch parg := parg.(type) { | |||
31 | case *Tree: | |||
32 | if targ, ok := targ.(*Tree); ok { | |||
33 | if !targ.Match(parg) { | |||
34 | return false | |||
35 | } | |||
36 | } else { | |||
37 | return false | |||
38 | } | |||
39 | case **string: | |||
40 | if targ, ok := targ.(string); ok { | |||
41 | if *parg == nil { | |||
42 | *parg = &targ | |||
43 | } else if **parg != targ { | |||
44 | return false | |||
45 | } | |||
46 | } else { | |||
47 | return false | |||
48 | } | |||
49 | default: | |||
50 | return false | |||
51 | } | |||
52 | } | |||
53 | return true | |||
54 | } | |||
55 | ||||
56 | func (t *Tree) String() string { | 16 | func (t *Tree) String() string { | |
57 | s := "(" + t.name | 17 | s := "(" + t.name | |
58 | for _, arg := range t.args { | 18 | for _, arg := range t.args { | |
59 | if arg, ok := arg.(*Tree); ok { | 19 | if arg, ok := arg.(*Tree); ok { | |
60 | s += " " + (*arg).String() | 20 | s += " " + (*arg).String() | |
61 | continue | 21 | continue | |
62 | } | 22 | } | |
63 | if arg, ok := arg.(string); ok { | 23 | if arg, ok := arg.(string); ok { | |
64 | s += fmt.Sprintf(" %q", arg) | 24 | s += fmt.Sprintf(" %q", arg) | |
65 | continue | 25 | continue | |
66 | } else { | |||
67 | s += fmt.Sprintf(" %v", arg) | |||
68 | } | 26 | } | |
27 | s += fmt.Sprintf(" %v", arg) | |||
69 | } | 28 | } | |
70 | return s + ")" | 29 | return s + ")" | |
71 | } | 30 | } | |
31 | ||||
32 | func (t *Tree) Visit(nodename string, action func(t *Tree)) { | |||
33 | if t.name == nodename { | |||
34 | action(t) | |||
35 | } | |||
36 | for _, arg := range t.args { | |||
37 | if child, ok := arg.(*Tree); ok { | |||
38 | child.Visit(nodename, action) | |||
39 | } | |||
40 | } | |||
41 | } |
@@ -1,28 +1,9 @@ | @@ -1,28 +1,9 @@ | |||
1 | package main | 1 | package main | |
2 | 2 | |||
3 | import ( | 3 | import ( | |
4 | check "gopkg.in/check.v1" | 4 | check "gopkg.in/check.v1" | |
5 | ) | 5 | ) | |
6 | 6 | |||
7 | func (s *Suite) TestTreeMatch_successful(c *check.C) { | |||
8 | var varname *string | |||
9 | pattern := NewTree("not", NewTree("empty", &varname)) | |||
10 | tree := NewTree("not", NewTree("empty", "VARNAME")) | |||
11 | ||||
12 | c.Check(tree.Match(pattern), equals, true) | |||
13 | c.Assert(varname, check.Not(equals), nil) | |||
14 | c.Check(*varname, equals, "VARNAME") | |||
15 | } | |||
16 | ||||
17 | func (s *Suite) TestTreeMatch_fails(c *check.C) { | |||
18 | var varname *string | |||
19 | pattern := NewTree("not", NewTree("empty", &varname)) | |||
20 | tree := NewTree("not", NewTree("full", "VARNAME")) | |||
21 | ||||
22 | c.Check(tree.Match(pattern), equals, false) | |||
23 | c.Check(varname, equals, (*string)(nil)) | |||
24 | } | |||
25 | ||||
26 | func (s *Suite) TestTreeString(c *check.C) { | 7 | func (s *Suite) TestTreeString(c *check.C) { | |
27 | c.Check(NewTree("not", NewTree("empty", "varname")).String(), equals, "(not (empty \"varname\"))") | 8 | c.Check(NewTree("not", NewTree("empty", "varname")).String(), equals, "(not (empty \"varname\"))") | |
28 | } | 9 | } |
@@ -19,27 +19,27 @@ import ( | @@ -19,27 +19,27 @@ import ( | |||
19 | func (gd *GlobalData) InitVartypes() { | 19 | func (gd *GlobalData) InitVartypes() { | |
20 | usr("ALLOW_VULNERABLE_PACKAGES", lkNone, CheckvarYes) | 20 | usr("ALLOW_VULNERABLE_PACKAGES", lkNone, CheckvarYes) | |
21 | usr("MANINSTALL", lkShell, enum("maninstall catinstall")) | 21 | usr("MANINSTALL", lkShell, enum("maninstall catinstall")) | |
22 | usr("MANZ", lkNone, CheckvarYes) | 22 | usr("MANZ", lkNone, CheckvarYes) | |
23 | usr("GZIP", lkShell, CheckvarShellWord) | 23 | usr("GZIP", lkShell, CheckvarShellWord) | |
24 | usr("MKCRYPTO", lkNone, CheckvarYesNo) | 24 | usr("MKCRYPTO", lkNone, CheckvarYesNo) | |
25 | usr("OBJHOSTNAME", lkNone, CheckvarYes) | 25 | usr("OBJHOSTNAME", lkNone, CheckvarYes) | |
26 | usr("OBJMACHINE", lkNone, CheckvarYes) | 26 | usr("OBJMACHINE", lkNone, CheckvarYes) | |
27 | usr("PKG_SUFX", lkNone, CheckvarFilename) | 27 | usr("PKG_SUFX", lkNone, CheckvarFilename) | |
28 | usr("PKGSRC_LOCKTYPE", lkNone, enum("none sleep once")) | 28 | usr("PKGSRC_LOCKTYPE", lkNone, enum("none sleep once")) | |
29 | usr("PKGSRC_SLEEPSECS", lkNone, CheckvarInteger) | 29 | usr("PKGSRC_SLEEPSECS", lkNone, CheckvarInteger) | |
30 | usr("USETBL", lkNone, CheckvarYes) | 30 | usr("USETBL", lkNone, CheckvarYes) | |
31 | usr("ABI", lkNone, enum("32 64")) | 31 | usr("ABI", lkNone, enum("32 64")) | |
32 | usr("PKG_DEVELOPER", lkNone, CheckvarYes) | 32 | usr("PKG_DEVELOPER", lkNone, CheckvarYesNo) | |
33 | usr("USE_ABI_DEPENDS", lkNone, CheckvarYesNo) | 33 | usr("USE_ABI_DEPENDS", lkNone, CheckvarYesNo) | |
34 | usr("PKG_REGISTER_SHELLS", lkNone, enum("YES NO")) | 34 | usr("PKG_REGISTER_SHELLS", lkNone, enum("YES NO")) | |
35 | usr("PKGSRC_COMPILER", lkShell, enum("ccache ccc clang distcc f2c gcc hp icc ido gcc mipspro mipspro-ucode pcc sunpro xlc")) | 35 | usr("PKGSRC_COMPILER", lkShell, enum("ccache ccc clang distcc f2c gcc hp icc ido gcc mipspro mipspro-ucode pcc sunpro xlc")) | |
36 | usr("PKGSRC_MESSAGE_RECIPIENTS", lkShell, CheckvarMailAddress) | 36 | usr("PKGSRC_MESSAGE_RECIPIENTS", lkShell, CheckvarMailAddress) | |
37 | usr("PKGSRC_SHOW_BUILD_DEFS", lkNone, CheckvarYesNo) | 37 | usr("PKGSRC_SHOW_BUILD_DEFS", lkNone, CheckvarYesNo) | |
38 | usr("PKGSRC_SHOW_PATCH_ERRORMSG", lkNone, CheckvarYesNo) | 38 | usr("PKGSRC_SHOW_PATCH_ERRORMSG", lkNone, CheckvarYesNo) | |
39 | usr("PKGSRC_RUN_TEST", lkNone, CheckvarYesNo) | 39 | usr("PKGSRC_RUN_TEST", lkNone, CheckvarYesNo) | |
40 | usr("PREFER_PKGSRC", lkShell, CheckvarIdentifier) | 40 | usr("PREFER_PKGSRC", lkShell, CheckvarIdentifier) | |
41 | usr("PREFER_NATIVE", lkShell, CheckvarIdentifier) | 41 | usr("PREFER_NATIVE", lkShell, CheckvarIdentifier) | |
42 | usr("PREFER_NATIVE_PTHREADS", lkNone, CheckvarYesNo) | 42 | usr("PREFER_NATIVE_PTHREADS", lkNone, CheckvarYesNo) | |
43 | usr("LOCALBASE", lkNone, CheckvarPathname) | 43 | usr("LOCALBASE", lkNone, CheckvarPathname) | |
44 | usr("CROSSBASE", lkNone, CheckvarPathname) | 44 | usr("CROSSBASE", lkNone, CheckvarPathname) | |
45 | usr("VARBASE", lkNone, CheckvarPathname) | 45 | usr("VARBASE", lkNone, CheckvarPathname) | |
@@ -260,31 +260,31 @@ func (gd *GlobalData) InitVartypes() { | @@ -260,31 +260,31 @@ func (gd *GlobalData) InitVartypes() { | |||
260 | sys("EMACS_LISPPREFIX", lkNone, CheckvarPathname) | 260 | sys("EMACS_LISPPREFIX", lkNone, CheckvarPathname) | |
261 | acl("EMACS_MODULES", lkShell, CheckvarIdentifier, "Makefile, Makefile.common: set, append") | 261 | acl("EMACS_MODULES", lkShell, CheckvarIdentifier, "Makefile, Makefile.common: set, append") | |
262 | sys("EMACS_PKGNAME_PREFIX", lkNone, CheckvarIdentifier) // Or the empty string. | 262 | sys("EMACS_PKGNAME_PREFIX", lkNone, CheckvarIdentifier) // Or the empty string. | |
263 | sys("EMACS_TYPE", lkNone, enum("emacs xemacs")) | 263 | sys("EMACS_TYPE", lkNone, enum("emacs xemacs")) | |
264 | acl("EMACS_USE_LEIM", lkNone, CheckvarYes, "") | 264 | acl("EMACS_USE_LEIM", lkNone, CheckvarYes, "") | |
265 | acl("EMACS_VERSIONS_ACCEPTED", lkShell, enum("emacs25 emacs24 emacs24nox emacs23 emacs23nox emacs22 emacs22nox emacs21 emacs21nox emacs20 xemacs215 xemacs215nox xemacs214 xemacs214nox"), "Makefile: set") | 265 | acl("EMACS_VERSIONS_ACCEPTED", lkShell, enum("emacs25 emacs24 emacs24nox emacs23 emacs23nox emacs22 emacs22nox emacs21 emacs21nox emacs20 xemacs215 xemacs215nox xemacs214 xemacs214nox"), "Makefile: set") | |
266 | sys("EMACS_VERSION_MAJOR", lkNone, CheckvarInteger) | 266 | sys("EMACS_VERSION_MAJOR", lkNone, CheckvarInteger) | |
267 | sys("EMACS_VERSION_MINOR", lkNone, CheckvarInteger) | 267 | sys("EMACS_VERSION_MINOR", lkNone, CheckvarInteger) | |
268 | acl("EMACS_VERSION_REQD", lkShell, enum("emacs24 emacs24nox emacs23 emacs23nox emacs22 emacs22nox emacs21 emacs21nox emacs20 xemacs215 xemacs214"), "Makefile: set, append") | 268 | acl("EMACS_VERSION_REQD", lkShell, enum("emacs24 emacs24nox emacs23 emacs23nox emacs22 emacs22nox emacs21 emacs21nox emacs20 xemacs215 xemacs214"), "Makefile: set, append") | |
269 | sys("EMULDIR", lkNone, CheckvarPathname) | 269 | sys("EMULDIR", lkNone, CheckvarPathname) | |
270 | sys("EMULSUBDIR", lkNone, CheckvarPathname) | 270 | sys("EMULSUBDIR", lkNone, CheckvarPathname) | |
271 | sys("OPSYS_EMULDIR", lkNone, CheckvarPathname) | 271 | sys("OPSYS_EMULDIR", lkNone, CheckvarPathname) | |
272 | sys("EMULSUBDIRSLASH", lkNone, CheckvarPathname) | 272 | sys("EMULSUBDIRSLASH", lkNone, CheckvarPathname) | |
273 | sys("EMUL_ARCH", lkNone, enum("i386 none x86_64")) | 273 | sys("EMUL_ARCH", lkNone, enum("arm i386 m68k none ns32k sparc vax x86_64")) | |
274 | sys("EMUL_DISTRO", lkNone, CheckvarIdentifier) | 274 | sys("EMUL_DISTRO", lkNone, CheckvarIdentifier) | |
275 | sys("EMUL_IS_NATIVE", lkNone, CheckvarYes) | 275 | sys("EMUL_IS_NATIVE", lkNone, CheckvarYes) | |
276 | pkg("EMUL_MODULES.*", lkShell, CheckvarIdentifier) | 276 | pkg("EMUL_MODULES.*", lkShell, CheckvarIdentifier) | |
277 | sys("EMUL_OPSYS", lkNone, enum("freebsd hpux irix linux osf1 solaris sunos none")) | 277 | sys("EMUL_OPSYS", lkNone, enum("darwin freebsd hpux irix linux osf1 solaris sunos none")) | |
278 | pkg("EMUL_PKG_FMT", lkNone, enum("plain rpm")) | 278 | pkg("EMUL_PKG_FMT", lkNone, enum("plain rpm")) | |
279 | usr("EMUL_PLATFORM", lkNone, CheckvarEmulPlatform) | 279 | usr("EMUL_PLATFORM", lkNone, CheckvarEmulPlatform) | |
280 | pkg("EMUL_PLATFORMS", lkShell, CheckvarEmulPlatform) | 280 | pkg("EMUL_PLATFORMS", lkShell, CheckvarEmulPlatform) | |
281 | usr("EMUL_PREFER", lkShell, CheckvarEmulPlatform) | 281 | usr("EMUL_PREFER", lkShell, CheckvarEmulPlatform) | |
282 | pkg("EMUL_REQD", lkSpace, CheckvarDependency) | 282 | pkg("EMUL_REQD", lkSpace, CheckvarDependency) | |
283 | usr("EMUL_TYPE.*", lkNone, enum("native builtin suse suse-9.1 suse-9.x suse-10.0 suse-10.x")) | 283 | usr("EMUL_TYPE.*", lkNone, enum("native builtin suse suse-9.1 suse-9.x suse-10.0 suse-10.x")) | |
284 | sys("ERROR_CAT", lkNone, CheckvarShellCommand) | 284 | sys("ERROR_CAT", lkNone, CheckvarShellCommand) | |
285 | sys("ERROR_MSG", lkNone, CheckvarShellCommand) | 285 | sys("ERROR_MSG", lkNone, CheckvarShellCommand) | |
286 | acl("EVAL_PREFIX", lkSpace, CheckvarShellWord, "Makefile, Makefile.common: append") // XXX: Combining ShellWord with lkSpace looks weird. | 286 | acl("EVAL_PREFIX", lkSpace, CheckvarShellWord, "Makefile, Makefile.common: append") // XXX: Combining ShellWord with lkSpace looks weird. | |
287 | sys("EXPORT_SYMBOLS_LDFLAGS", lkShell, CheckvarLdFlag) | 287 | sys("EXPORT_SYMBOLS_LDFLAGS", lkShell, CheckvarLdFlag) | |
288 | sys("EXTRACT_CMD", lkNone, CheckvarShellCommand) | 288 | sys("EXTRACT_CMD", lkNone, CheckvarShellCommand) | |
289 | pkg("EXTRACT_DIR", lkNone, CheckvarPathname) | 289 | pkg("EXTRACT_DIR", lkNone, CheckvarPathname) | |
290 | pkglist("EXTRACT_ELEMENTS", lkShell, CheckvarPathmask) | 290 | pkglist("EXTRACT_ELEMENTS", lkShell, CheckvarPathmask) | |
@@ -527,27 +527,27 @@ func (gd *GlobalData) InitVartypes() { | @@ -527,27 +527,27 @@ func (gd *GlobalData) InitVartypes() { | |||
527 | cmdline("PKG_DEBUG_LEVEL", lkNone, CheckvarInteger) | 527 | cmdline("PKG_DEBUG_LEVEL", lkNone, CheckvarInteger) | |
528 | usr("PKG_DEFAULT_OPTIONS", lkShell, CheckvarOption) | 528 | usr("PKG_DEFAULT_OPTIONS", lkShell, CheckvarOption) | |
529 | sys("PKG_DELETE", lkNone, CheckvarShellCommand) | 529 | sys("PKG_DELETE", lkNone, CheckvarShellCommand) | |
530 | acl("PKG_DESTDIR_SUPPORT", lkShell, enum("destdir user-destdir"), "Makefile, Makefile.common: set") | 530 | acl("PKG_DESTDIR_SUPPORT", lkShell, enum("destdir user-destdir"), "Makefile, Makefile.common: set") | |
531 | pkglist("PKG_FAIL_REASON", lkShell, CheckvarShellWord) | 531 | pkglist("PKG_FAIL_REASON", lkShell, CheckvarShellWord) | |
532 | acl("PKG_GECOS.*", lkNone, CheckvarMessage, "Makefile: set") | 532 | acl("PKG_GECOS.*", lkNone, CheckvarMessage, "Makefile: set") | |
533 | acl("PKG_GID.*", lkNone, CheckvarInteger, "Makefile: set") | 533 | acl("PKG_GID.*", lkNone, CheckvarInteger, "Makefile: set") | |
534 | acl("PKG_GROUPS", lkShell, CheckvarShellWord, "Makefile: set, append") | 534 | acl("PKG_GROUPS", lkShell, CheckvarShellWord, "Makefile: set, append") | |
535 | pkglist("PKG_GROUPS_VARS", lkShell, CheckvarVarname) | 535 | pkglist("PKG_GROUPS_VARS", lkShell, CheckvarVarname) | |
536 | acl("PKG_HOME.*", lkNone, CheckvarPathname, "Makefile: set") | 536 | acl("PKG_HOME.*", lkNone, CheckvarPathname, "Makefile: set") | |
537 | acl("PKG_HACKS", lkShell, CheckvarIdentifier, "hacks.mk: append") | 537 | acl("PKG_HACKS", lkShell, CheckvarIdentifier, "hacks.mk: append") | |
538 | sys("PKG_INFO", lkNone, CheckvarShellCommand) | 538 | sys("PKG_INFO", lkNone, CheckvarShellCommand) | |
539 | sys("PKG_JAVA_HOME", lkNone, CheckvarPathname) | 539 | sys("PKG_JAVA_HOME", lkNone, CheckvarPathname) | |
540 | jvms := enum("blackdown-jdk13 jdk jdk14 kaffe run-jdk13 sun-jdk14 sun-jdk15 sun-jdk6 openjdk7 openjdk7-bin sun-jdk7") | 540 | jvms := enum("blackdown-jdk13 jdk jdk14 kaffe run-jdk13 sun-jdk14 sun-jdk15 sun-jdk6 openjdk7 openjdk7-bin sun-jdk7 openjdk8 oracle-jdk8") | |
541 | sys("PKG_JVM", lkNone, jvms) | 541 | sys("PKG_JVM", lkNone, jvms) | |
542 | acl("PKG_JVMS_ACCEPTED", lkShell, jvms, "Makefile: set; Makefile.common: default, set") | 542 | acl("PKG_JVMS_ACCEPTED", lkShell, jvms, "Makefile: set; Makefile.common: default, set") | |
543 | usr("PKG_JVM_DEFAULT", lkNone, jvms) | 543 | usr("PKG_JVM_DEFAULT", lkNone, jvms) | |
544 | acl("PKG_LEGACY_OPTIONS", lkShell, CheckvarOption, "") | 544 | acl("PKG_LEGACY_OPTIONS", lkShell, CheckvarOption, "") | |
545 | acl("PKG_LIBTOOL", lkNone, CheckvarPathname, "Makefile: set") | 545 | acl("PKG_LIBTOOL", lkNone, CheckvarPathname, "Makefile: set") | |
546 | acl("PKG_OPTIONS", lkSpace, CheckvarOption, "bsd.options.mk: set; *: use-loadtime, use") | 546 | acl("PKG_OPTIONS", lkSpace, CheckvarOption, "bsd.options.mk: set; *: use-loadtime, use") | |
547 | usr("PKG_OPTIONS.*", lkSpace, CheckvarOption) | 547 | usr("PKG_OPTIONS.*", lkSpace, CheckvarOption) | |
548 | acl("PKG_OPTIONS_DEPRECATED_WARNINGS", lkShell, CheckvarShellWord, "") | 548 | acl("PKG_OPTIONS_DEPRECATED_WARNINGS", lkShell, CheckvarShellWord, "") | |
549 | acl("PKG_OPTIONS_GROUP.*", lkSpace, CheckvarOption, "options.mk: set; Makefile: set") | 549 | acl("PKG_OPTIONS_GROUP.*", lkSpace, CheckvarOption, "options.mk: set; Makefile: set") | |
550 | acl("PKG_OPTIONS_LEGACY_OPTS", lkSpace, CheckvarUnchecked, "Makefile, Makefile.common: append; options.mk: append") | 550 | acl("PKG_OPTIONS_LEGACY_OPTS", lkSpace, CheckvarUnchecked, "Makefile, Makefile.common: append; options.mk: append") | |
551 | acl("PKG_OPTIONS_LEGACY_VARS", lkSpace, CheckvarUnchecked, "Makefile, Makefile.common: append; options.mk: append") | 551 | acl("PKG_OPTIONS_LEGACY_VARS", lkSpace, CheckvarUnchecked, "Makefile, Makefile.common: append; options.mk: append") | |
552 | acl("PKG_OPTIONS_NONEMPTY_SETS", lkSpace, CheckvarIdentifier, "") | 552 | acl("PKG_OPTIONS_NONEMPTY_SETS", lkSpace, CheckvarIdentifier, "") | |
553 | acl("PKG_OPTIONS_OPTIONAL_GROUPS", lkSpace, CheckvarIdentifier, "options.mk: set, append") | 553 | acl("PKG_OPTIONS_OPTIONAL_GROUPS", lkSpace, CheckvarIdentifier, "options.mk: set, append") | |
@@ -577,27 +577,27 @@ func (gd *GlobalData) InitVartypes() { | @@ -577,27 +577,27 @@ func (gd *GlobalData) InitVartypes() { | |||
577 | acl("PLIST_TYPE", lkNone, enum("dynamic static"), "") | 577 | acl("PLIST_TYPE", lkNone, enum("dynamic static"), "") | |
578 | acl("PREPEND_PATH", lkShell, CheckvarPathname, "") | 578 | acl("PREPEND_PATH", lkShell, CheckvarPathname, "") | |
579 | acl("PREFIX", lkNone, CheckvarPathname, "*: use") | 579 | acl("PREFIX", lkNone, CheckvarPathname, "*: use") | |
580 | acl("PREV_PKGPATH", lkNone, CheckvarPathname, "*: use") // doesn't exist any longer | 580 | acl("PREV_PKGPATH", lkNone, CheckvarPathname, "*: use") // doesn't exist any longer | |
581 | acl("PRINT_PLIST_AWK", lkNone, CheckvarAwkCommand, "*: append") | 581 | acl("PRINT_PLIST_AWK", lkNone, CheckvarAwkCommand, "*: append") | |
582 | acl("PRIVILEGED_STAGES", lkShell, enum("install package clean"), "") | 582 | acl("PRIVILEGED_STAGES", lkShell, enum("install package clean"), "") | |
583 | acl("PTHREAD_AUTO_VARS", lkNone, CheckvarYesNo, "Makefile: set") | 583 | acl("PTHREAD_AUTO_VARS", lkNone, CheckvarYesNo, "Makefile: set") | |
584 | sys("PTHREAD_CFLAGS", lkShell, CheckvarCFlag) | 584 | sys("PTHREAD_CFLAGS", lkShell, CheckvarCFlag) | |
585 | sys("PTHREAD_LDFLAGS", lkShell, CheckvarLdFlag) | 585 | sys("PTHREAD_LDFLAGS", lkShell, CheckvarLdFlag) | |
586 | sys("PTHREAD_LIBS", lkShell, CheckvarLdFlag) | 586 | sys("PTHREAD_LIBS", lkShell, CheckvarLdFlag) | |
587 | acl("PTHREAD_OPTS", lkShell, enum("native optional require"), "Makefile: set, append; Makefile.common: append; buildlink3.mk: append") | 587 | acl("PTHREAD_OPTS", lkShell, enum("native optional require"), "Makefile: set, append; Makefile.common: append; buildlink3.mk: append") | |
588 | sys("PTHREAD_TYPE", lkNone, CheckvarIdentifier) // Or "native" or "none". | 588 | sys("PTHREAD_TYPE", lkNone, CheckvarIdentifier) // Or "native" or "none". | |
589 | pkg("PY_PATCHPLIST", lkNone, CheckvarYes) | 589 | pkg("PY_PATCHPLIST", lkNone, CheckvarYes) | |
590 | acl("PYPKGPREFIX", lkNone, enum("py27 py33 py34"), "pyversion.mk: set; *: use-loadtime, use") | 590 | acl("PYPKGPREFIX", lkNone, enum("py27 py33 py34 py35"), "pyversion.mk: set; *: use-loadtime, use") | |
591 | pkg("PYTHON_FOR_BUILD_ONLY", lkNone, CheckvarYes) | 591 | pkg("PYTHON_FOR_BUILD_ONLY", lkNone, CheckvarYes) | |
592 | pkglist("REPLACE_PYTHON", lkShell, CheckvarPathmask) | 592 | pkglist("REPLACE_PYTHON", lkShell, CheckvarPathmask) | |
593 | pkg("PYTHON_VERSIONS_ACCEPTED", lkShell, CheckvarVersion) | 593 | pkg("PYTHON_VERSIONS_ACCEPTED", lkShell, CheckvarVersion) | |
594 | pkg("PYTHON_VERSIONS_INCOMPATIBLE", lkShell, CheckvarVersion) | 594 | pkg("PYTHON_VERSIONS_INCOMPATIBLE", lkShell, CheckvarVersion) | |
595 | usr("PYTHON_VERSION_DEFAULT", lkNone, CheckvarVersion) | 595 | usr("PYTHON_VERSION_DEFAULT", lkNone, CheckvarVersion) | |
596 | usr("PYTHON_VERSION_REQD", lkNone, CheckvarVersion) | 596 | usr("PYTHON_VERSION_REQD", lkNone, CheckvarVersion) | |
597 | pkglist("PYTHON_VERSIONED_DEPENDENCIES", lkShell, CheckvarPythonDependency) | 597 | pkglist("PYTHON_VERSIONED_DEPENDENCIES", lkShell, CheckvarPythonDependency) | |
598 | sys("RANLIB", lkNone, CheckvarShellCommand) | 598 | sys("RANLIB", lkNone, CheckvarShellCommand) | |
599 | pkglist("RCD_SCRIPTS", lkShell, CheckvarFilename) | 599 | pkglist("RCD_SCRIPTS", lkShell, CheckvarFilename) | |
600 | acl("RCD_SCRIPT_SRC.*", lkShell, CheckvarPathname, "Makefile: set") | 600 | acl("RCD_SCRIPT_SRC.*", lkShell, CheckvarPathname, "Makefile: set") | |
601 | acl("REPLACE.*", lkNone, CheckvarString, "Makefile: set") | 601 | acl("REPLACE.*", lkNone, CheckvarString, "Makefile: set") | |
602 | pkglist("REPLACE_AWK", lkShell, CheckvarPathmask) | 602 | pkglist("REPLACE_AWK", lkShell, CheckvarPathmask) | |
603 | pkglist("REPLACE_BASH", lkShell, CheckvarPathmask) | 603 | pkglist("REPLACE_BASH", lkShell, CheckvarPathmask) | |
@@ -660,26 +660,27 @@ func (gd *GlobalData) InitVartypes() { | @@ -660,26 +660,27 @@ func (gd *GlobalData) InitVartypes() { | |||
660 | sys("TOOLS_PATH.*", lkNone, CheckvarPathname) | 660 | sys("TOOLS_PATH.*", lkNone, CheckvarPathname) | |
661 | sys("TOOLS_PLATFORM.*", lkNone, CheckvarShellCommand) | 661 | sys("TOOLS_PLATFORM.*", lkNone, CheckvarShellCommand) | |
662 | sys("TOUCH_FLAGS", lkShell, CheckvarShellWord) | 662 | sys("TOUCH_FLAGS", lkShell, CheckvarShellWord) | |
663 | pkglist("UAC_REQD_EXECS", lkShell, CheckvarPrefixPathname) | 663 | pkglist("UAC_REQD_EXECS", lkShell, CheckvarPrefixPathname) | |
664 | acl("UNLIMIT_RESOURCES", lkShell, enum("datasize stacksize memorysize"), "Makefile: set, append; Makefile.common: append") | 664 | acl("UNLIMIT_RESOURCES", lkShell, enum("datasize stacksize memorysize"), "Makefile: set, append; Makefile.common: append") | |
665 | usr("UNPRIVILEGED_USER", lkNone, CheckvarUserGroupName) | 665 | usr("UNPRIVILEGED_USER", lkNone, CheckvarUserGroupName) | |
666 | usr("UNPRIVILEGED_GROUP", lkNone, CheckvarUserGroupName) | 666 | usr("UNPRIVILEGED_GROUP", lkNone, CheckvarUserGroupName) | |
667 | pkglist("UNWRAP_FILES", lkShell, CheckvarPathmask) | 667 | pkglist("UNWRAP_FILES", lkShell, CheckvarPathmask) | |
668 | usr("UPDATE_TARGET", lkShell, CheckvarIdentifier) | 668 | usr("UPDATE_TARGET", lkShell, CheckvarIdentifier) | |
669 | pkg("USE_BSD_MAKEFILE", lkNone, CheckvarYes) | 669 | pkg("USE_BSD_MAKEFILE", lkNone, CheckvarYes) | |
670 | acl("USE_BUILTIN.*", lkNone, CheckvarYesNoIndirectly, "builtin.mk: set") | 670 | acl("USE_BUILTIN.*", lkNone, CheckvarYesNoIndirectly, "builtin.mk: set") | |
671 | pkg("USE_CMAKE", lkNone, CheckvarYes) | 671 | pkg("USE_CMAKE", lkNone, CheckvarYes) | |
672 | acl("USE_CROSSBASE", lkNone, CheckvarYes, "Makefile: set") | 672 | acl("USE_CROSSBASE", lkNone, CheckvarYes, "Makefile: set") | |
673 | usr("USE_DESTDIR", lkNone, CheckvarYes) | |||
673 | pkg("USE_FEATURES", lkShell, CheckvarIdentifier) | 674 | pkg("USE_FEATURES", lkShell, CheckvarIdentifier) | |
674 | pkg("USE_GCC_RUNTIME", lkNone, CheckvarYesNo) | 675 | pkg("USE_GCC_RUNTIME", lkNone, CheckvarYesNo) | |
675 | pkg("USE_GNU_CONFIGURE_HOST", lkNone, CheckvarYesNo) | 676 | pkg("USE_GNU_CONFIGURE_HOST", lkNone, CheckvarYesNo) | |
676 | acl("USE_GNU_ICONV", lkNone, CheckvarYes, "Makefile, Makefile.common: set; options.mk: set") | 677 | acl("USE_GNU_ICONV", lkNone, CheckvarYes, "Makefile, Makefile.common: set; options.mk: set") | |
677 | acl("USE_IMAKE", lkNone, CheckvarYes, "Makefile: set") | 678 | acl("USE_IMAKE", lkNone, CheckvarYes, "Makefile: set") | |
678 | pkg("USE_JAVA", lkNone, enum("run yes build")) | 679 | pkg("USE_JAVA", lkNone, enum("run yes build")) | |
679 | pkg("USE_JAVA2", lkNone, enum("YES yes no 1.4 1.5 6 7 8")) | 680 | pkg("USE_JAVA2", lkNone, enum("YES yes no 1.4 1.5 6 7 8")) | |
680 | acl("USE_LANGUAGES", lkShell, enum("ada c c99 c++ fortran fortran77 java objc"), "Makefile, Makefile.common, options.mk: set") | 681 | acl("USE_LANGUAGES", lkShell, enum("ada c c99 c++ fortran fortran77 java objc"), "Makefile, Makefile.common, options.mk: set") | |
681 | pkg("USE_LIBTOOL", lkNone, CheckvarYes) | 682 | pkg("USE_LIBTOOL", lkNone, CheckvarYes) | |
682 | pkg("USE_MAKEINFO", lkNone, CheckvarYes) | 683 | pkg("USE_MAKEINFO", lkNone, CheckvarYes) | |
683 | pkg("USE_MSGFMT_PLURALS", lkNone, CheckvarYes) | 684 | pkg("USE_MSGFMT_PLURALS", lkNone, CheckvarYes) | |
684 | pkg("USE_NCURSES", lkNone, CheckvarYes) | 685 | pkg("USE_NCURSES", lkNone, CheckvarYes) | |
685 | pkg("USE_OLD_DES_API", lkNone, CheckvarYesNo) | 686 | pkg("USE_OLD_DES_API", lkNone, CheckvarYesNo) | |
@@ -695,29 +696,46 @@ func (gd *GlobalData) InitVartypes() { | @@ -695,29 +696,46 @@ func (gd *GlobalData) InitVartypes() { | |||
695 | sys("WRKDIR", lkNone, CheckvarPathname) | 696 | sys("WRKDIR", lkNone, CheckvarPathname) | |
696 | pkg("WRKSRC", lkNone, CheckvarWrkdirSubdirectory) | 697 | pkg("WRKSRC", lkNone, CheckvarWrkdirSubdirectory) | |
697 | sys("X11_PKGSRCDIR.*", lkNone, CheckvarPathname) | 698 | sys("X11_PKGSRCDIR.*", lkNone, CheckvarPathname) | |
698 | usr("XAW_TYPE", lkNone, enum("3d neXtaw standard xpm")) | 699 | usr("XAW_TYPE", lkNone, enum("3d neXtaw standard xpm")) | |
699 | acl("XMKMF_FLAGS", lkShell, CheckvarShellWord, "") | 700 | acl("XMKMF_FLAGS", lkShell, CheckvarShellWord, "") | |
700 | } | 701 | } | |
701 | 702 | |||
702 | func enum(values string) *VarChecker { | 703 | func enum(values string) *VarChecker { | |
703 | vmap := make(map[string]bool) | 704 | vmap := make(map[string]bool) | |
704 | for _, value := range splitOnSpace(values) { | 705 | for _, value := range splitOnSpace(values) { | |
705 | vmap[value] = true | 706 | vmap[value] = true | |
706 | } | 707 | } | |
707 | name := "enum: " + values + " " // See IsEnum | 708 | name := "enum: " + values + " " // See IsEnum | |
708 | return &VarChecker{name, func(ctx *VartypeCheck) { | 709 | return &VarChecker{name, func(cv *VartypeCheck) { | |
709 | if !vmap[ctx.value] { | 710 | if cv.op == opUseMatch { | |
710 | ctx.line.Warnf("%q is not valid for %s. Use one of { %s } instead.", ctx.value, ctx.varname, values) | 711 | if !vmap[cv.value] && cv.value == cv.valueNovar { | |
712 | canMatch := false | |||
713 | for value := range vmap { | |||
714 | if ok, err := path.Match(cv.value, value); err != nil { | |||
715 | cv.line.Warnf("Invalid match pattern %q.", cv.value) | |||
716 | } else if ok { | |||
717 | canMatch = true | |||
718 | } | |||
719 | } | |||
720 | if !canMatch { | |||
721 | cv.line.Warnf("The pattern %q cannot match any of { %s } for %s.", cv.value, values, cv.varname) | |||
722 | } | |||
723 | } | |||
724 | return | |||
725 | } | |||
726 | ||||
727 | if !vmap[cv.value] { | |||
728 | cv.line.Warnf("%q is not valid for %s. Use one of { %s } instead.", cv.value, cv.varname, values) | |||
711 | } | 729 | } | |
712 | }} | 730 | }} | |
713 | } | 731 | } | |
714 | 732 | |||
715 | func acl(varname string, kindOfList KindOfList, checker *VarChecker, aclentries string) { | 733 | func acl(varname string, kindOfList KindOfList, checker *VarChecker, aclentries string) { | |
716 | m := mustMatch(varname, `^([A-Z_.][A-Z0-9_]*)(|\*|\.\*)$`) | 734 | m := mustMatch(varname, `^([A-Z_.][A-Z0-9_]*)(|\*|\.\*)$`) | |
717 | varbase, varparam := m[1], m[2] | 735 | varbase, varparam := m[1], m[2] | |
718 | 736 | |||
719 | vtype := &Vartype{kindOfList, checker, parseAclEntries(varname, aclentries), false} | 737 | vtype := &Vartype{kindOfList, checker, parseAclEntries(varname, aclentries), false} | |
720 | 738 | |||
721 | if G.globalData.vartypes == nil { | 739 | if G.globalData.vartypes == nil { | |
722 | G.globalData.vartypes = make(map[string]*Vartype) | 740 | G.globalData.vartypes = make(map[string]*Vartype) | |
723 | } | 741 | } |
@@ -15,48 +15,49 @@ type VartypeCheck struct { | @@ -15,48 +15,49 @@ type VartypeCheck struct { | |||
15 | comment string | 15 | comment string | |
16 | listContext bool | 16 | listContext bool | |
17 | guessed bool // Whether the type definition is guessed (based on the variable name) or explicitly defined (see vardefs.go). | 17 | guessed bool // Whether the type definition is guessed (based on the variable name) or explicitly defined (see vardefs.go). | |
18 | } | 18 | } | |
19 | 19 | |||
20 | type MkOperator uint8 | 20 | type MkOperator uint8 | |
21 | 21 | |||
22 | const ( | 22 | const ( | |
23 | opAssign MkOperator = iota // = | 23 | opAssign MkOperator = iota // = | |
24 | opAssignShell // != | 24 | opAssignShell // != | |
25 | opAssignEval // := | 25 | opAssignEval // := | |
26 | opAssignAppend // += | 26 | opAssignAppend // += | |
27 | opAssignDefault // ?= | 27 | opAssignDefault // ?= | |
28 | opUseLoadtime | 28 | opUse // | |
29 | opUse | 29 | opUseLoadtime // | |
30 | opUseMatch // Used in the :M operator | |||
30 | ) | 31 | ) | |
31 | 32 | |||
32 | func NewMkOperator(op string) MkOperator { | 33 | func NewMkOperator(op string) MkOperator { | |
33 | switch op { | 34 | switch op { | |
34 | case "=": | 35 | case "=": | |
35 | return opAssign | 36 | return opAssign | |
36 | case "!=": | 37 | case "!=": | |
37 | return opAssignShell | 38 | return opAssignShell | |
38 | case ":=": | 39 | case ":=": | |
39 | return opAssignEval | 40 | return opAssignEval | |
40 | case "+=": | 41 | case "+=": | |
41 | return opAssignAppend | 42 | return opAssignAppend | |
42 | case "?=": | 43 | case "?=": | |
43 | return opAssignDefault | 44 | return opAssignDefault | |
44 | } | 45 | } | |
45 | return opAssign | 46 | return opAssign | |
46 | } | 47 | } | |
47 | 48 | |||
48 | func (op MkOperator) String() string { | 49 | func (op MkOperator) String() string { | |
49 | return [...]string{"=", "!=", ":=", "+=", "?=", "use", "use-loadtime"}[op] | 50 | return [...]string{"=", "!=", ":=", "+=", "?=", "use", "use-loadtime", "use-match"}[op] | |
50 | } | 51 | } | |
51 | 52 | |||
52 | func (cv *VartypeCheck) AwkCommand() { | 53 | func (cv *VartypeCheck) AwkCommand() { | |
53 | if G.opts.DebugUnchecked { | 54 | if G.opts.DebugUnchecked { | |
54 | cv.line.Debug1("Unchecked AWK command: %q", cv.value) | 55 | cv.line.Debug1("Unchecked AWK command: %q", cv.value) | |
55 | } | 56 | } | |
56 | } | 57 | } | |
57 | 58 | |||
58 | func (cv *VartypeCheck) BasicRegularExpression() { | 59 | func (cv *VartypeCheck) BasicRegularExpression() { | |
59 | if G.opts.DebugUnchecked { | 60 | if G.opts.DebugUnchecked { | |
60 | cv.line.Debug1("Unchecked basic regular expression: %q", cv.value) | 61 | cv.line.Debug1("Unchecked basic regular expression: %q", cv.value) | |
61 | } | 62 | } | |
62 | } | 63 | } | |
@@ -81,26 +82,29 @@ func (cv *VartypeCheck) Category() { | @@ -81,26 +82,29 @@ func (cv *VartypeCheck) Category() { | |||
81 | "packages", "perl5", "plan9", "python", | 82 | "packages", "perl5", "plan9", "python", | |
82 | "ruby", | 83 | "ruby", | |
83 | "scm", | 84 | "scm", | |
84 | "tcl", "tk", | 85 | "tcl", "tk", | |
85 | "windowmaker", | 86 | "windowmaker", | |
86 | "xmms": | 87 | "xmms": | |
87 | default: | 88 | default: | |
88 | cv.line.Error1("Invalid category %q.", cv.value) | 89 | cv.line.Error1("Invalid category %q.", cv.value) | |
89 | } | 90 | } | |
90 | } | 91 | } | |
91 | 92 | |||
92 | // A single option to the C/C++ compiler. | 93 | // A single option to the C/C++ compiler. | |
93 | func (cv *VartypeCheck) CFlag() { | 94 | func (cv *VartypeCheck) CFlag() { | |
95 | if cv.op == opUseMatch { | |||
96 | return | |||
97 | } | |||
94 | cflag := cv.value | 98 | cflag := cv.value | |
95 | switch { | 99 | switch { | |
96 | case matches(cflag, `^-[DILOUWfgm]`), | 100 | case matches(cflag, `^-[DILOUWfgm]`), | |
97 | hasPrefix(cflag, "-std="), | 101 | hasPrefix(cflag, "-std="), | |
98 | cflag == "-c99", | 102 | cflag == "-c99", | |
99 | cflag == "-c", | 103 | cflag == "-c", | |
100 | cflag == "-no-integrated-as", | 104 | cflag == "-no-integrated-as", | |
101 | cflag == "-pthread", | 105 | cflag == "-pthread", | |
102 | hasPrefix(cflag, "`") && hasSuffix(cflag, "`"), | 106 | hasPrefix(cflag, "`") && hasSuffix(cflag, "`"), | |
103 | containsVarRef(cflag): | 107 | containsVarRef(cflag): | |
104 | return | 108 | return | |
105 | case hasPrefix(cflag, "-"): | 109 | case hasPrefix(cflag, "-"): | |
106 | cv.line.Warn1("Unknown compiler flag %q.", cflag) | 110 | cv.line.Warn1("Unknown compiler flag %q.", cflag) | |
@@ -281,71 +285,85 @@ func (cv *VartypeCheck) FetchURL() { | @@ -281,71 +285,85 @@ func (cv *VartypeCheck) FetchURL() { | |||
281 | if !G.globalData.MasterSiteVars[name] { | 285 | if !G.globalData.MasterSiteVars[name] { | |
282 | cv.line.Error1("%s does not exist.", name) | 286 | cv.line.Error1("%s does not exist.", name) | |
283 | } | 287 | } | |
284 | if !hasSuffix(subdir, "/") { | 288 | if !hasSuffix(subdir, "/") { | |
285 | cv.line.Error1("The subdirectory in %s must end with a slash.", name) | 289 | cv.line.Error1("The subdirectory in %s must end with a slash.", name) | |
286 | } | 290 | } | |
287 | } | 291 | } | |
288 | } | 292 | } | |
289 | 293 | |||
290 | // See Pathname | 294 | // See Pathname | |
291 | // See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_169 | 295 | // See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_169 | |
292 | func (cv *VartypeCheck) Filename() { | 296 | func (cv *VartypeCheck) Filename() { | |
293 | switch { | 297 | switch { | |
298 | case cv.op == opUseMatch: | |||
299 | break | |||
294 | case contains(cv.valueNovar, "/"): | 300 | case contains(cv.valueNovar, "/"): | |
295 | cv.line.Warn0("A filename should not contain a slash.") | 301 | cv.line.Warn0("A filename should not contain a slash.") | |
296 | case !matches(cv.valueNovar, `^[-0-9@A-Za-z.,_~+%]*$`): | 302 | case !matches(cv.valueNovar, `^[-0-9@A-Za-z.,_~+%]*$`): | |
297 | cv.line.Warn1("%q is not a valid filename.", cv.value) | 303 | cv.line.Warn1("%q is not a valid filename.", cv.value) | |
298 | } | 304 | } | |
299 | } | 305 | } | |
300 | 306 | |||
301 | func (cv *VartypeCheck) Filemask() { | 307 | func (cv *VartypeCheck) Filemask() { | |
308 | if cv.op == opUseMatch { | |||
309 | return | |||
310 | } | |||
302 | if !matches(cv.valueNovar, `^[-0-9A-Za-z._~+%*?]*$`) { | 311 | if !matches(cv.valueNovar, `^[-0-9A-Za-z._~+%*?]*$`) { | |
303 | cv.line.Warn1("%q is not a valid filename mask.", cv.value) | 312 | cv.line.Warn1("%q is not a valid filename mask.", cv.value) | |
304 | } | 313 | } | |
305 | } | 314 | } | |
306 | 315 | |||
307 | func (cv *VartypeCheck) FileMode() { | 316 | func (cv *VartypeCheck) FileMode() { | |
308 | switch { | 317 | switch { | |
309 | case cv.value != "" && cv.valueNovar == "": | 318 | case cv.value != "" && cv.valueNovar == "": | |
310 | // Fine. | 319 | // Fine. | |
311 | case matches(cv.value, `^[0-7]{3,4}`): | 320 | case matches(cv.value, `^[0-7]{3,4}`): | |
312 | // Fine. | 321 | // Fine. | |
313 | default: | 322 | default: | |
314 | cv.line.Warn1("Invalid file mode %q.", cv.value) | 323 | cv.line.Warn1("Invalid file mode %q.", cv.value) | |
315 | } | 324 | } | |
316 | } | 325 | } | |
317 | 326 | |||
318 | func (cv *VartypeCheck) Identifier() { | 327 | func (cv *VartypeCheck) Identifier() { | |
328 | if cv.op == opUseMatch { | |||
329 | if cv.value == cv.valueNovar && !matches(cv.value, `^[\w*?]`) { | |||
330 | cv.line.Warn2("Invalid identifier pattern %q for %s.", cv.value, cv.varname) | |||
331 | } | |||
332 | return | |||
333 | } | |||
319 | if cv.value != cv.valueNovar { | 334 | if cv.value != cv.valueNovar { | |
320 | //line.logWarning("Identifiers should be given directly.") | 335 | //line.logWarning("Identifiers should be given directly.") | |
321 | } | 336 | } | |
322 | switch { | 337 | switch { | |
323 | case matches(cv.valueNovar, `^[+\-.0-9A-Z_a-z]+$`): | 338 | case matches(cv.valueNovar, `^[+\-.0-9A-Z_a-z]+$`): | |
324 | // Fine. | 339 | // Fine. | |
325 | case cv.value != "" && cv.valueNovar == "": | 340 | case cv.value != "" && cv.valueNovar == "": | |
326 | // Don't warn here. | 341 | // Don't warn here. | |
327 | default: | 342 | default: | |
328 | cv.line.Warn1("Invalid identifier %q.", cv.value) | 343 | cv.line.Warn1("Invalid identifier %q.", cv.value) | |
329 | } | 344 | } | |
330 | } | 345 | } | |
331 | 346 | |||
332 | func (cv *VartypeCheck) Integer() { | 347 | func (cv *VartypeCheck) Integer() { | |
333 | if !matches(cv.value, `^\d+$`) { | 348 | if !matches(cv.value, `^\d+$`) { | |
334 | cv.line.Warn1("Invalid integer %q.", cv.value) | 349 | cv.line.Warn1("Invalid integer %q.", cv.value) | |
335 | } | 350 | } | |
336 | } | 351 | } | |
337 | 352 | |||
338 | func (cv *VartypeCheck) LdFlag() { | 353 | func (cv *VartypeCheck) LdFlag() { | |
354 | if cv.op == opUseMatch { | |||
355 | return | |||
356 | } | |||
339 | ldflag := cv.value | 357 | ldflag := cv.value | |
340 | if m, rpathFlag := match1(ldflag, `^(-Wl,(?:-R|-rpath|--rpath))`); m { | 358 | if m, rpathFlag := match1(ldflag, `^(-Wl,(?:-R|-rpath|--rpath))`); m { | |
341 | cv.line.Warn1("Please use \"${COMPILER_RPATH_FLAG}\" instead of %q.", rpathFlag) | 359 | cv.line.Warn1("Please use \"${COMPILER_RPATH_FLAG}\" instead of %q.", rpathFlag) | |
342 | return | 360 | return | |
343 | } | 361 | } | |
344 | 362 | |||
345 | switch { | 363 | switch { | |
346 | case hasPrefix(ldflag, "-L"), | 364 | case hasPrefix(ldflag, "-L"), | |
347 | hasPrefix(ldflag, "-l"), | 365 | hasPrefix(ldflag, "-l"), | |
348 | ldflag == "-pthread", | 366 | ldflag == "-pthread", | |
349 | ldflag == "-static", | 367 | ldflag == "-static", | |
350 | hasPrefix(ldflag, "-static-"), | 368 | hasPrefix(ldflag, "-static-"), | |
351 | hasPrefix(ldflag, "-Wl,-"), | 369 | hasPrefix(ldflag, "-Wl,-"), | |
@@ -442,49 +460,55 @@ func (cv *VartypeCheck) Pathlist() { | @@ -442,49 +460,55 @@ func (cv *VartypeCheck) Pathlist() { | |||
442 | if !matches(path, `^[-0-9A-Za-z._~+%/]*$`) { | 460 | if !matches(path, `^[-0-9A-Za-z._~+%/]*$`) { | |
443 | cv.line.Warn1("%q is not a valid pathname.", path) | 461 | cv.line.Warn1("%q is not a valid pathname.", path) | |
444 | } | 462 | } | |
445 | 463 | |||
446 | if !hasPrefix(path, "/") { | 464 | if !hasPrefix(path, "/") { | |
447 | cv.line.Warn2("All components of %s (in this case %q) should be absolute paths.", cv.varname, path) | 465 | cv.line.Warn2("All components of %s (in this case %q) should be absolute paths.", cv.varname, path) | |
448 | } | 466 | } | |
449 | } | 467 | } | |
450 | } | 468 | } | |
451 | 469 | |||
452 | // Shell globbing including slashes. | 470 | // Shell globbing including slashes. | |
453 | // See Filemask | 471 | // See Filemask | |
454 | func (cv *VartypeCheck) Pathmask() { | 472 | func (cv *VartypeCheck) Pathmask() { | |
473 | if cv.op == opUseMatch { | |||
474 | return | |||
475 | } | |||
455 | if !matches(cv.valueNovar, `^[#\-0-9A-Za-z._~+%*?/\[\]]*`) { | 476 | if !matches(cv.valueNovar, `^[#\-0-9A-Za-z._~+%*?/\[\]]*`) { | |
456 | cv.line.Warn1("%q is not a valid pathname mask.", cv.value) | 477 | cv.line.Warn1("%q is not a valid pathname mask.", cv.value) | |
457 | } | 478 | } | |
458 | cv.line.CheckAbsolutePathname(cv.value) | 479 | cv.line.CheckAbsolutePathname(cv.value) | |
459 | } | 480 | } | |
460 | 481 | |||
461 | // Like Filename, but including slashes | 482 | // Like Filename, but including slashes | |
462 | // See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_266 | 483 | // See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_266 | |
463 | func (cv *VartypeCheck) Pathname() { | 484 | func (cv *VartypeCheck) Pathname() { | |
485 | if cv.op == opUseMatch { | |||
486 | return | |||
487 | } | |||
464 | if !matches(cv.valueNovar, `^[#\-0-9A-Za-z._~+%/]*$`) { | 488 | if !matches(cv.valueNovar, `^[#\-0-9A-Za-z._~+%/]*$`) { | |
465 | cv.line.Warn1("%q is not a valid pathname.", cv.value) | 489 | cv.line.Warn1("%q is not a valid pathname.", cv.value) | |
466 | } | 490 | } | |
467 | cv.line.CheckAbsolutePathname(cv.value) | 491 | cv.line.CheckAbsolutePathname(cv.value) | |
468 | } | 492 | } | |
469 | 493 | |||
470 | func (cv *VartypeCheck) Perl5Packlist() { | 494 | func (cv *VartypeCheck) Perl5Packlist() { | |
471 | if cv.value != cv.valueNovar { | 495 | if cv.value != cv.valueNovar { | |
472 | cv.line.Warn1("%s should not depend on other variables.", cv.varname) | 496 | cv.line.Warn1("%s should not depend on other variables.", cv.varname) | |
473 | } | 497 | } | |
474 | } | 498 | } | |
475 | 499 | |||
476 | func (cv *VartypeCheck) PkgName() { | 500 | func (cv *VartypeCheck) PkgName() { | |
477 | if cv.value == cv.valueNovar && !matches(cv.value, rePkgname) { | 501 | if cv.op != opUseMatch && cv.value == cv.valueNovar && !matches(cv.value, rePkgname) { | |
478 | cv.line.Warn1("%q is not a valid package name. A valid package name has the form packagename-version, where version consists only of digits, letters and dots.", cv.value) | 502 | cv.line.Warn1("%q is not a valid package name. A valid package name has the form packagename-version, where version consists only of digits, letters and dots.", cv.value) | |
479 | } | 503 | } | |
480 | } | 504 | } | |
481 | 505 | |||
482 | func (cv *VartypeCheck) PkgOptionsVar() { | 506 | func (cv *VartypeCheck) PkgOptionsVar() { | |
483 | cv.mkline.CheckVartypePrimitive(cv.varname, CheckvarVarname, cv.op, cv.value, cv.comment, false, cv.guessed) | 507 | cv.mkline.CheckVartypePrimitive(cv.varname, CheckvarVarname, cv.op, cv.value, cv.comment, false, cv.guessed) | |
484 | if matches(cv.value, `\$\{PKGBASE[:\}]`) { | 508 | if matches(cv.value, `\$\{PKGBASE[:\}]`) { | |
485 | cv.line.Error0("PKGBASE must not be used in PKG_OPTIONS_VAR.") | 509 | cv.line.Error0("PKGBASE must not be used in PKG_OPTIONS_VAR.") | |
486 | Explain3( | 510 | Explain3( | |
487 | "PKGBASE is defined in bsd.pkg.mk, which is included as the", | 511 | "PKGBASE is defined in bsd.pkg.mk, which is included as the", | |
488 | "very last file, but PKG_OPTIONS_VAR is evaluated earlier.", | 512 | "very last file, but PKG_OPTIONS_VAR is evaluated earlier.", | |
489 | "Use ${PKGNAME:C/-[0-9].*//} instead.") | 513 | "Use ${PKGNAME:C/-[0-9].*//} instead.") | |
490 | } | 514 | } | |
@@ -632,26 +656,29 @@ func (cv *VartypeCheck) SedCommands() { | @@ -632,26 +656,29 @@ func (cv *VartypeCheck) SedCommands() { | |||
632 | case token == "-n": | 656 | case token == "-n": | |
633 | // Don't print lines per default. | 657 | // Don't print lines per default. | |
634 | 658 | |||
635 | case i == 0 && matches(token, `^(["']?)(?:\d*|/.*/)s.+["']?$`): | 659 | case i == 0 && matches(token, `^(["']?)(?:\d*|/.*/)s.+["']?$`): | |
636 | line.Note0("Please always use \"-e\" in sed commands, even if there is only one substitution.") | 660 | line.Note0("Please always use \"-e\" in sed commands, even if there is only one substitution.") | |
637 | 661 | |||
638 | default: | 662 | default: | |
639 | line.Warn1("Unknown sed command %q.", token) | 663 | line.Warn1("Unknown sed command %q.", token) | |
640 | } | 664 | } | |
641 | } | 665 | } | |
642 | } | 666 | } | |
643 | 667 | |||
644 | func (cv *VartypeCheck) ShellCommand() { | 668 | func (cv *VartypeCheck) ShellCommand() { | |
669 | if cv.op == opUseMatch { | |||
670 | return | |||
671 | } | |||
645 | setE := true | 672 | setE := true | |
646 | NewShellLine(cv.mkline).CheckShellCommand(cv.value, &setE) | 673 | NewShellLine(cv.mkline).CheckShellCommand(cv.value, &setE) | |
647 | } | 674 | } | |
648 | 675 | |||
649 | // Zero or more shell commands, each terminated with a semicolon. | 676 | // Zero or more shell commands, each terminated with a semicolon. | |
650 | func (cv *VartypeCheck) ShellCommands() { | 677 | func (cv *VartypeCheck) ShellCommands() { | |
651 | NewShellLine(cv.mkline).CheckShellCommands(cv.value) | 678 | NewShellLine(cv.mkline).CheckShellCommands(cv.value) | |
652 | } | 679 | } | |
653 | 680 | |||
654 | func (cv *VartypeCheck) ShellWord() { | 681 | func (cv *VartypeCheck) ShellWord() { | |
655 | if !cv.listContext { | 682 | if !cv.listContext { | |
656 | NewShellLine(cv.mkline).CheckToken(cv.value, true) | 683 | NewShellLine(cv.mkline).CheckToken(cv.value, true) | |
657 | } | 684 | } | |
@@ -670,27 +697,27 @@ func (cv *VartypeCheck) String() { | @@ -670,27 +697,27 @@ func (cv *VartypeCheck) String() { | |||
670 | func (cv *VartypeCheck) Tool() { | 697 | func (cv *VartypeCheck) Tool() { | |
671 | if cv.varname == "TOOLS_NOOP" && cv.op == opAssignAppend { | 698 | if cv.varname == "TOOLS_NOOP" && cv.op == opAssignAppend { | |
672 | // no warning for package-defined tool definitions | 699 | // no warning for package-defined tool definitions | |
673 | 700 | |||
674 | } else if m, toolname, tooldep := match2(cv.value, `^([-\w]+|\[)(?::(\w+))?$`); m { | 701 | } else if m, toolname, tooldep := match2(cv.value, `^([-\w]+|\[)(?::(\w+))?$`); m { | |
675 | if !G.globalData.Tools[toolname] { | 702 | if !G.globalData.Tools[toolname] { | |
676 | cv.line.Error1("Unknown tool %q.", toolname) | 703 | cv.line.Error1("Unknown tool %q.", toolname) | |
677 | } | 704 | } | |
678 | switch tooldep { | 705 | switch tooldep { | |
679 | case "", "bootstrap", "build", "pkgsrc", "run": | 706 | case "", "bootstrap", "build", "pkgsrc", "run": | |
680 | default: | 707 | default: | |
681 | cv.line.Error1("Unknown tool dependency %q. Use one of \"build\", \"pkgsrc\" or \"run\".", tooldep) | 708 | cv.line.Error1("Unknown tool dependency %q. Use one of \"build\", \"pkgsrc\" or \"run\".", tooldep) | |
682 | } | 709 | } | |
683 | } else { | 710 | } else if cv.op != opUseMatch { | |
684 | cv.line.Error1("Invalid tool syntax: %q.", cv.value) | 711 | cv.line.Error1("Invalid tool syntax: %q.", cv.value) | |
685 | } | 712 | } | |
686 | } | 713 | } | |
687 | 714 | |||
688 | func (cv *VartypeCheck) Unchecked() { | 715 | func (cv *VartypeCheck) Unchecked() { | |
689 | // Do nothing, as the name says. | 716 | // Do nothing, as the name says. | |
690 | } | 717 | } | |
691 | 718 | |||
692 | func (cv *VartypeCheck) URL() { | 719 | func (cv *VartypeCheck) URL() { | |
693 | line, value := cv.line, cv.value | 720 | line, value := cv.line, cv.value | |
694 | 721 | |||
695 | if value == "" && hasPrefix(cv.comment, "#") { | 722 | if value == "" && hasPrefix(cv.comment, "#") { | |
696 | // Ok | 723 | // Ok | |
@@ -731,27 +758,31 @@ func (cv *VartypeCheck) Varname() { | @@ -731,27 +758,31 @@ func (cv *VartypeCheck) Varname() { | |||
731 | cv.line.Warn1("%q is not a valid variable name.", cv.value) | 758 | cv.line.Warn1("%q is not a valid variable name.", cv.value) | |
732 | Explain( | 759 | Explain( | |
733 | "Variable names are restricted to only uppercase letters and the", | 760 | "Variable names are restricted to only uppercase letters and the", | |
734 | "underscore in the basename, and arbitrary characters in the", | 761 | "underscore in the basename, and arbitrary characters in the", | |
735 | "parameterized part, following the dot.", | 762 | "parameterized part, following the dot.", | |
736 | "", | 763 | "", | |
737 | "Examples:", | 764 | "Examples:", | |
738 | "\t* PKGNAME", | 765 | "\t* PKGNAME", | |
739 | "\t* PKG_OPTIONS.gnuchess") | 766 | "\t* PKG_OPTIONS.gnuchess") | |
740 | } | 767 | } | |
741 | } | 768 | } | |
742 | 769 | |||
743 | func (cv *VartypeCheck) Version() { | 770 | func (cv *VartypeCheck) Version() { | |
744 | if !matches(cv.value, `^([\d.])+$`) { | 771 | if cv.op == opUseMatch { | |
772 | if !matches(cv.value, `^[\d?\[][\w\-.*?\[\]]+$`) { | |||
773 | cv.line.Warn1("Invalid version number pattern %q.", cv.value) | |||
774 | } | |||
775 | } else if cv.value == cv.valueNovar && !matches(cv.value, `^\d[\w.]+$`) { | |||
745 | cv.line.Warn1("Invalid version number %q.", cv.value) | 776 | cv.line.Warn1("Invalid version number %q.", cv.value) | |
746 | } | 777 | } | |
747 | } | 778 | } | |
748 | 779 | |||
749 | func (cv *VartypeCheck) WrapperReorder() { | 780 | func (cv *VartypeCheck) WrapperReorder() { | |
750 | if !matches(cv.value, `^reorder:l:([\w\-]+):([\w\-]+)$`) { | 781 | if !matches(cv.value, `^reorder:l:([\w\-]+):([\w\-]+)$`) { | |
751 | cv.line.Warn1("Unknown wrapper reorder command %q.", cv.value) | 782 | cv.line.Warn1("Unknown wrapper reorder command %q.", cv.value) | |
752 | } | 783 | } | |
753 | } | 784 | } | |
754 | 785 | |||
755 | func (cv *VartypeCheck) WrapperTransform() { | 786 | func (cv *VartypeCheck) WrapperTransform() { | |
756 | cmd := cv.value | 787 | cmd := cv.value | |
757 | if hasPrefix(cmd, "rm:-") || | 788 | if hasPrefix(cmd, "rm:-") || | |
@@ -776,41 +807,57 @@ func (cv *VartypeCheck) WrksrcSubdirecto | @@ -776,41 +807,57 @@ func (cv *VartypeCheck) WrksrcSubdirecto | |||
776 | } | 807 | } | |
777 | cv.line.Note2("You can use %q instead of %q.", rest, cv.value) | 808 | cv.line.Note2("You can use %q instead of %q.", rest, cv.value) | |
778 | Explain1( | 809 | Explain1( | |
779 | "These directories are interpreted relative to ${WRKSRC}.") | 810 | "These directories are interpreted relative to ${WRKSRC}.") | |
780 | 811 | |||
781 | } else if cv.value != "" && cv.valueNovar == "" { | 812 | } else if cv.value != "" && cv.valueNovar == "" { | |
782 | // The value of another variable | 813 | // The value of another variable | |
783 | 814 | |||
784 | } else if !matches(cv.valueNovar, `^(?:\.|[0-9A-Za-z_@][-0-9A-Za-z_@./+]*)$`) { | 815 | } else if !matches(cv.valueNovar, `^(?:\.|[0-9A-Za-z_@][-0-9A-Za-z_@./+]*)$`) { | |
785 | cv.line.Warn1("%q is not a valid subdirectory of ${WRKSRC}.", cv.value) | 816 | cv.line.Warn1("%q is not a valid subdirectory of ${WRKSRC}.", cv.value) | |
786 | } | 817 | } | |
787 | } | 818 | } | |
788 | 819 | |||
789 | // Used for variables that are checked using `.if defined(VAR)`. | |||
790 | func (cv *VartypeCheck) Yes() { | 820 | func (cv *VartypeCheck) Yes() { | |
791 | if !matches(cv.value, `^(?:YES|yes)(?:\s+#.*)?$`) { | 821 | switch cv.op { | |
792 | cv.line.Warn1("%s should be set to YES or yes.", cv.varname) | 822 | case opUseMatch: | |
793 | Explain4( | 823 | cv.line.Warn1("%s should only be used in a \".if defined(...)\" conditional.", cv.varname) | |
794 | "This variable means \"yes\" if it is defined, and \"no\" if it is", | 824 | Explain( | |
795 | "undefined. Even when it has the value \"no\", this means \"yes\".", | 825 | "This variable can have only two values: defined or undefined.", | |
796 | "Therefore when it is defined, its value should correspond to its", | 826 | "When it is defined, it means \"yes\", even when its value is", | |
797 | "meaning.") | 827 | "\"no\" or the empty string.", | |
828 | "", | |||
829 | "Therefore, it should not be checked by comparing its value", | |||
830 | "but using \".if defined(VARNAME)\" alone.") | |||
831 | ||||
832 | default: | |||
833 | if !matches(cv.value, `^(?:YES|yes)(?:\s+#.*)?$`) { | |||
834 | cv.line.Warn1("%s should be set to YES or yes.", cv.varname) | |||
835 | Explain4( | |||
836 | "This variable means \"yes\" if it is defined, and \"no\" if it is", | |||
837 | "undefined. Even when it has the value \"no\", this means \"yes\".", | |||
838 | "Therefore when it is defined, its value should correspond to its", | |||
839 | "meaning.") | |||
840 | } | |||
798 | } | 841 | } | |
799 | } | 842 | } | |
800 | 843 | |||
801 | // The type YesNo is used for variables that are checked using | |||
802 | // .if defined(VAR) && !empty(VAR:M[Yy][Ee][Ss]) | |||
803 | // | |||
804 | func (cv *VartypeCheck) YesNo() { | 844 | func (cv *VartypeCheck) YesNo() { | |
805 | if !matches(cv.value, `^(?:YES|yes|NO|no)(?:\s+#.*)?$`) { | 845 | if cv.op == opUseMatch { | |
846 | switch cv.value { | |||
847 | case "[yY][eE][sS]": | |||
848 | case "[Yy][Ee][Ss]": | |||
849 | case "[nN][oO]": | |||
850 | case "[Nn][Oo]": | |||
851 | default: | |||
852 | cv.line.Warnf("%s should be matched against %q or %q, not %q.", cv.varname, "[yY][eE][sS]", "[nN][oO]", cv.value) | |||
853 | } | |||
854 | } else if !matches(cv.value, `^(?:YES|yes|NO|no)(?:\s+#.*)?$`) { | |||
806 | cv.line.Warn1("%s should be set to YES, yes, NO, or no.", cv.varname) | 855 | cv.line.Warn1("%s should be set to YES, yes, NO, or no.", cv.varname) | |
807 | } | 856 | } | |
808 | } | 857 | } | |
809 | 858 | |||
810 | // Like YesNo, but the value may be produced by a shell command using the | |||
811 | // != operator. | |||
812 | func (cv *VartypeCheck) YesNoIndirectly() { | 859 | func (cv *VartypeCheck) YesNoIndirectly() { | |
813 | if cv.valueNovar != "" && !matches(cv.value, `^(?:YES|yes|NO|no)(?:\s+#.*)?$`) { | 860 | if cv.valueNovar != "" { | |
814 | cv.line.Warn1("%s should be set to YES, yes, NO, or no.", cv.varname) | 861 | cv.YesNo() | |
815 | } | 862 | } | |
816 | } | 863 | } |
@@ -1,16 +1,18 @@ | @@ -1,16 +1,18 @@ | |||
1 | package main | 1 | package main | |
2 | 2 | |||
3 | import ( | 3 | import ( | |
4 | "fmt" | |||
5 | ||||
4 | check "gopkg.in/check.v1" | 6 | check "gopkg.in/check.v1" | |
5 | ) | 7 | ) | |
6 | 8 | |||
7 | func (s *Suite) TestVartypeCheck_AwkCommand(c *check.C) { | 9 | func (s *Suite) TestVartypeCheck_AwkCommand(c *check.C) { | |
8 | s.UseCommandLine(c, "-Dunchecked") | 10 | s.UseCommandLine(c, "-Dunchecked") | |
9 | runVartypeChecks("PLIST_AWK", opAssignAppend, (*VartypeCheck).AwkCommand, | 11 | runVartypeChecks("PLIST_AWK", opAssignAppend, (*VartypeCheck).AwkCommand, | |
10 | "{print $0}", | 12 | "{print $0}", | |
11 | "{print $$0}") | 13 | "{print $$0}") | |
12 | 14 | |||
13 | c.Check(s.Output(), equals, ""+ | 15 | c.Check(s.Output(), equals, ""+ | |
14 | "ERROR: fname:1: Invalid Makefile syntax at \"$0}\".\n"+ | 16 | "ERROR: fname:1: Invalid Makefile syntax at \"$0}\".\n"+ | |
15 | "DEBUG: fname:1: Unchecked AWK command: \"{print $0}\"\n"+ | 17 | "DEBUG: fname:1: Unchecked AWK command: \"{print $0}\"\n"+ | |
16 | "DEBUG: fname:2: Unchecked AWK command: \"{print $$0}\"\n") | 18 | "DEBUG: fname:2: Unchecked AWK command: \"{print $$0}\"\n") | |
@@ -159,26 +161,36 @@ func (s *Suite) TestVartypeCheck_DistSuf | @@ -159,26 +161,36 @@ func (s *Suite) TestVartypeCheck_DistSuf | |||
159 | 161 | |||
160 | func (s *Suite) TestVartypeCheck_EmulPlatform(c *check.C) { | 162 | func (s *Suite) TestVartypeCheck_EmulPlatform(c *check.C) { | |
161 | runVartypeChecks("EMUL_PLATFORM", opAssign, (*VartypeCheck).EmulPlatform, | 163 | runVartypeChecks("EMUL_PLATFORM", opAssign, (*VartypeCheck).EmulPlatform, | |
162 | "linux-i386", | 164 | "linux-i386", | |
163 | "nextbsd-8087", | 165 | "nextbsd-8087", | |
164 | "${LINUX}") | 166 | "${LINUX}") | |
165 | 167 | |||
166 | c.Check(s.Output(), equals, ""+ | 168 | c.Check(s.Output(), equals, ""+ | |
167 | "WARN: fname:2: Unknown operating system: nextbsd\n"+ | 169 | "WARN: fname:2: Unknown operating system: nextbsd\n"+ | |
168 | "WARN: fname:2: Unknown hardware architecture: 8087\n"+ | 170 | "WARN: fname:2: Unknown hardware architecture: 8087\n"+ | |
169 | "WARN: fname:3: \"${LINUX}\" is not a valid emulation platform.\n") | 171 | "WARN: fname:3: \"${LINUX}\" is not a valid emulation platform.\n") | |
170 | } | 172 | } | |
171 | 173 | |||
174 | func (s *Suite) TestVartypeCheck_Enum(c *check.C) { | |||
175 | runVartypeMatchChecks("JDK", enum("jdk1 jdk2 jdk4").checker, | |||
176 | "*", | |||
177 | "jdk*", | |||
178 | "sun-jdk*", | |||
179 | "${JDKNAME}") | |||
180 | ||||
181 | c.Check(s.Output(), equals, "WARN: fname:3: The pattern \"sun-jdk*\" cannot match any of { jdk1 jdk2 jdk4 } for JDK.\n") | |||
182 | } | |||
183 | ||||
172 | func (s *Suite) TestVartypeCheck_FetchURL(c *check.C) { | 184 | func (s *Suite) TestVartypeCheck_FetchURL(c *check.C) { | |
173 | G.globalData.MasterSiteUrls = map[string]string{ | 185 | G.globalData.MasterSiteUrls = map[string]string{ | |
174 | "https://github.com/": "MASTER_SITE_GITHUB", | 186 | "https://github.com/": "MASTER_SITE_GITHUB", | |
175 | "http://ftp.gnu.org/pub/gnu/": "MASTER_SITE_GNU", | 187 | "http://ftp.gnu.org/pub/gnu/": "MASTER_SITE_GNU", | |
176 | } | 188 | } | |
177 | G.globalData.MasterSiteVars = map[string]bool{ | 189 | G.globalData.MasterSiteVars = map[string]bool{ | |
178 | "MASTER_SITE_GITHUB": true, | 190 | "MASTER_SITE_GITHUB": true, | |
179 | "MASTER_SITE_GNU": true, | 191 | "MASTER_SITE_GNU": true, | |
180 | } | 192 | } | |
181 | 193 | |||
182 | runVartypeChecks("MASTER_SITES", opAssign, (*VartypeCheck).FetchURL, | 194 | runVartypeChecks("MASTER_SITES", opAssign, (*VartypeCheck).FetchURL, | |
183 | "https://github.com/example/project/", | 195 | "https://github.com/example/project/", | |
184 | "http://ftp.gnu.org/pub/gnu/bison", // Missing a slash at the end | 196 | "http://ftp.gnu.org/pub/gnu/bison", // Missing a slash at the end | |
@@ -353,55 +365,78 @@ func (s *Suite) TestVartypeCheck_Varname | @@ -353,55 +365,78 @@ func (s *Suite) TestVartypeCheck_Varname | |||
353 | 365 | |||
354 | c.Check(s.Output(), equals, "WARN: fname:2: \"VarBase\" is not a valid variable name.\n") | 366 | c.Check(s.Output(), equals, "WARN: fname:2: \"VarBase\" is not a valid variable name.\n") | |
355 | } | 367 | } | |
356 | 368 | |||
357 | func (s *Suite) TestVartypeCheck_Yes(c *check.C) { | 369 | func (s *Suite) TestVartypeCheck_Yes(c *check.C) { | |
358 | runVartypeChecks("APACHE_MODULE", opAssign, (*VartypeCheck).Yes, | 370 | runVartypeChecks("APACHE_MODULE", opAssign, (*VartypeCheck).Yes, | |
359 | "yes", | 371 | "yes", | |
360 | "no", | 372 | "no", | |
361 | "${YESVAR}") | 373 | "${YESVAR}") | |
362 | 374 | |||
363 | c.Check(s.Output(), equals, ""+ | 375 | c.Check(s.Output(), equals, ""+ | |
364 | "WARN: fname:2: APACHE_MODULE should be set to YES or yes.\n"+ | 376 | "WARN: fname:2: APACHE_MODULE should be set to YES or yes.\n"+ | |
365 | "WARN: fname:3: APACHE_MODULE should be set to YES or yes.\n") | 377 | "WARN: fname:3: APACHE_MODULE should be set to YES or yes.\n") | |
378 | ||||
379 | runVartypeMatchChecks("PKG_DEVELOPER", (*VartypeCheck).Yes, | |||
380 | "yes", | |||
381 | "no", | |||
382 | "${YESVAR}") | |||
383 | ||||
384 | c.Check(s.Output(), equals, ""+ | |||
385 | "WARN: fname:1: PKG_DEVELOPER should only be used in a \".if defined(...)\" conditional.\n"+ | |||
386 | "WARN: fname:2: PKG_DEVELOPER should only be used in a \".if defined(...)\" conditional.\n"+ | |||
387 | "WARN: fname:3: PKG_DEVELOPER should only be used in a \".if defined(...)\" conditional.\n") | |||
366 | } | 388 | } | |
367 | 389 | |||
368 | func (s *Suite) TestVartypeCheck_YesNo(c *check.C) { | 390 | func (s *Suite) TestVartypeCheck_YesNo(c *check.C) { | |
369 | runVartypeChecks("GNU_CONFIGURE", opAssign, (*VartypeCheck).YesNo, | 391 | runVartypeChecks("GNU_CONFIGURE", opAssign, (*VartypeCheck).YesNo, | |
370 | "yes", | 392 | "yes", | |
371 | "no", | 393 | "no", | |
372 | "ja", | 394 | "ja", | |
373 | "${YESVAR}") | 395 | "${YESVAR}") | |
374 | 396 | |||
375 | c.Check(s.Output(), equals, ""+ | 397 | c.Check(s.Output(), equals, ""+ | |
376 | "WARN: fname:3: GNU_CONFIGURE should be set to YES, yes, NO, or no.\n"+ | 398 | "WARN: fname:3: GNU_CONFIGURE should be set to YES, yes, NO, or no.\n"+ | |
377 | "WARN: fname:4: GNU_CONFIGURE should be set to YES, yes, NO, or no.\n") | 399 | "WARN: fname:4: GNU_CONFIGURE should be set to YES, yes, NO, or no.\n") | |
378 | } | 400 | } | |
379 | 401 | |||
380 | func (s *Suite) TestVartypeCheck_YesNoIndirectly(c *check.C) { | 402 | func (s *Suite) TestVartypeCheck_YesNoIndirectly(c *check.C) { | |
381 | runVartypeChecks("GNU_CONFIGURE", opAssign, (*VartypeCheck).YesNoIndirectly, | 403 | runVartypeChecks("GNU_CONFIGURE", opAssign, (*VartypeCheck).YesNoIndirectly, | |
382 | "yes", | 404 | "yes", | |
383 | "no", | 405 | "no", | |
384 | "ja", | 406 | "ja", | |
385 | "${YESVAR}") | 407 | "${YESVAR}") | |
386 | 408 | |||
387 | c.Check(s.Output(), equals, ""+ | 409 | c.Check(s.Output(), equals, ""+ | |
388 | "WARN: fname:3: GNU_CONFIGURE should be set to YES, yes, NO, or no.\n") | 410 | "WARN: fname:3: GNU_CONFIGURE should be set to YES, yes, NO, or no.\n") | |
389 | } | 411 | } | |
390 | 412 | |||
391 | func runVartypeChecks(varname string, op MkOperator, checker func(*VartypeCheck), values ...string) { | 413 | func runVartypeChecks(varname string, op MkOperator, checker func(*VartypeCheck), values ...string) { | |
414 | if !contains(op.String(), "=") { | |||
415 | panic("runVartypeChecks needs an assignment operator") | |||
416 | } | |||
392 | for i, value := range values { | 417 | for i, value := range values { | |
393 | mkline := NewMkLine(NewLine("fname", i+1, varname+op.String()+value, nil)) | 418 | mkline := NewMkLine(NewLine("fname", i+1, varname+op.String()+value, nil)) | |
394 | valueNovar := mkline.withoutMakeVariables(mkline.Value(), true) | 419 | valueNovar := mkline.withoutMakeVariables(mkline.Value(), true) | |
395 | vc := &VartypeCheck{mkline, mkline.Line, mkline.Varname(), mkline.Op(), mkline.Value(), valueNovar, "", true, false} | 420 | vc := &VartypeCheck{mkline, mkline.Line, mkline.Varname(), mkline.Op(), mkline.Value(), valueNovar, "", true, false} | |
396 | checker(vc) | 421 | checker(vc) | |
397 | } | 422 | } | |
398 | } | 423 | } | |
399 | 424 | |||
425 | func runVartypeMatchChecks(varname string, checker func(*VartypeCheck), values ...string) { | |||
426 | for i, value := range values { | |||
427 | text := fmt.Sprintf(".if ${%s:M%s} == \"\"", varname, value) | |||
428 | mkline := NewMkLine(NewLine("fname", i+1, text, nil)) | |||
429 | valueNovar := mkline.withoutMakeVariables(value, true) | |||
430 | vc := &VartypeCheck{mkline, mkline.Line, varname, opUseMatch, value, valueNovar, "", true, false} | |||
431 | checker(vc) | |||
432 | } | |||
433 | } | |||
434 | ||||
400 | func runVartypeChecksFname(fname, varname string, op MkOperator, checker func(*VartypeCheck), values ...string) { | 435 | func runVartypeChecksFname(fname, varname string, op MkOperator, checker func(*VartypeCheck), values ...string) { | |
401 | for i, value := range values { | 436 | for i, value := range values { | |
402 | mkline := NewMkLine(NewLine(fname, i+1, varname+op.String()+value, nil)) | 437 | mkline := NewMkLine(NewLine(fname, i+1, varname+op.String()+value, nil)) | |
403 | valueNovar := mkline.withoutMakeVariables(value, true) | 438 | valueNovar := mkline.withoutMakeVariables(value, true) | |
404 | vc := &VartypeCheck{mkline, mkline.Line, varname, op, value, valueNovar, "", true, false} | 439 | vc := &VartypeCheck{mkline, mkline.Line, varname, op, value, valueNovar, "", true, false} | |
405 | checker(vc) | 440 | checker(vc) | |
406 | } | 441 | } | |
407 | } | 442 | } |