version 1.55, 2019/04/03 21:49:51 |
version 1.56, 2019/04/20 17:43:24 |
|
|
package pkglint |
package pkglint |
|
|
import "gopkg.in/check.v1" |
import ( |
|
"gopkg.in/check.v1" |
|
"strings" |
|
) |
|
|
func (s *Suite) Test_NewMkLine__varassign(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__varassign(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mkline := t.NewMkLine("test.mk", 101, |
mkline := t.NewMkLine("test.mk", 101, |
Line 17 func (s *Suite) Test_NewMkLine__varassig |
|
Line 20 func (s *Suite) Test_NewMkLine__varassig |
|
c.Check(mkline.VarassignComment(), equals, "# varassign comment") |
c.Check(mkline.VarassignComment(), equals, "# varassign comment") |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__varassign_space_around_operator(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__varassign_space_around_operator(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
t.SetUpCommandLine("--show-autofix", "--source") |
t.SetUpCommandLine("--show-autofix", "--source") |
Line 31 func (s *Suite) Test_NewMkLine__varassig |
|
Line 34 func (s *Suite) Test_NewMkLine__varassig |
|
"+\tpkgbase= package") |
"+\tpkgbase= package") |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__shellcmd(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__shellcmd(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mkline := t.NewMkLine("test.mk", 101, |
mkline := t.NewMkLine("test.mk", 101, |
Line 41 func (s *Suite) Test_NewMkLine__shellcmd |
|
Line 44 func (s *Suite) Test_NewMkLine__shellcmd |
|
c.Check(mkline.ShellCommand(), equals, "shell command # shell comment") |
c.Check(mkline.ShellCommand(), equals, "shell command # shell comment") |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__comment(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__comment(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mkline := t.NewMkLine("test.mk", 101, |
mkline := t.NewMkLine("test.mk", 101, |
Line 50 func (s *Suite) Test_NewMkLine__comment( |
|
Line 53 func (s *Suite) Test_NewMkLine__comment( |
|
c.Check(mkline.IsComment(), equals, true) |
c.Check(mkline.IsComment(), equals, true) |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__empty(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__empty(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mkline := t.NewMkLine("test.mk", 101, "") |
mkline := t.NewMkLine("test.mk", 101, "") |
Line 58 func (s *Suite) Test_NewMkLine__empty(c |
|
Line 61 func (s *Suite) Test_NewMkLine__empty(c |
|
c.Check(mkline.IsEmpty(), equals, true) |
c.Check(mkline.IsEmpty(), equals, true) |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__directive(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__directive(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mkline := t.NewMkLine("test.mk", 101, |
mkline := t.NewMkLine("test.mk", 101, |
Line 71 func (s *Suite) Test_NewMkLine__directiv |
|
Line 74 func (s *Suite) Test_NewMkLine__directiv |
|
c.Check(mkline.DirectiveComment(), equals, "directive comment") |
c.Check(mkline.DirectiveComment(), equals, "directive comment") |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__include(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__include(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mkline := t.NewMkLine("test.mk", 101, |
mkline := t.NewMkLine("test.mk", 101, |
Line 85 func (s *Suite) Test_NewMkLine__include( |
|
Line 88 func (s *Suite) Test_NewMkLine__include( |
|
c.Check(mkline.IsSysinclude(), equals, false) |
c.Check(mkline.IsSysinclude(), equals, false) |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__sysinclude(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__sysinclude(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mkline := t.NewMkLine("test.mk", 101, |
mkline := t.NewMkLine("test.mk", 101, |
Line 99 func (s *Suite) Test_NewMkLine__sysinclu |
|
Line 102 func (s *Suite) Test_NewMkLine__sysinclu |
|
c.Check(mkline.IsInclude(), equals, false) |
c.Check(mkline.IsInclude(), equals, false) |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__dependency(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__dependency(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mkline := t.NewMkLine("test.mk", 101, |
mkline := t.NewMkLine("test.mk", 101, |
Line 110 func (s *Suite) Test_NewMkLine__dependen |
|
Line 113 func (s *Suite) Test_NewMkLine__dependen |
|
c.Check(mkline.Sources(), equals, "source1 source2") |
c.Check(mkline.Sources(), equals, "source1 source2") |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__dependency_space(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__dependency_space(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mkline := t.NewMkLine("test.mk", 101, |
mkline := t.NewMkLine("test.mk", 101, |
Line 122 func (s *Suite) Test_NewMkLine__dependen |
|
Line 125 func (s *Suite) Test_NewMkLine__dependen |
|
"NOTE: test.mk:101: Space before colon in dependency line.") |
"NOTE: test.mk:101: Space before colon in dependency line.") |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__varassign_append(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__varassign_append(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mkline := t.NewMkLine("test.mk", 101, |
mkline := t.NewMkLine("test.mk", 101, |
Line 134 func (s *Suite) Test_NewMkLine__varassig |
|
Line 137 func (s *Suite) Test_NewMkLine__varassig |
|
c.Check(mkline.Varparam(), equals, "") |
c.Check(mkline.Varparam(), equals, "") |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__merge_conflict(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__merge_conflict(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mkline := t.NewMkLine("test.mk", 101, |
mkline := t.NewMkLine("test.mk", 101, |
Line 151 func (s *Suite) Test_NewMkLine__merge_co |
|
Line 154 func (s *Suite) Test_NewMkLine__merge_co |
|
c.Check(mkline.IsSysinclude(), equals, false) |
c.Check(mkline.IsSysinclude(), equals, false) |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__autofix_space_after_varname(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__autofix_space_after_varname(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
t.SetUpCommandLine("-Wspace") |
t.SetUpCommandLine("-Wspace") |
Line 187 func (s *Suite) Test_NewMkLine__autofix_ |
|
Line 190 func (s *Suite) Test_NewMkLine__autofix_ |
|
"pkgbase := pkglint") |
"pkgbase := pkglint") |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__varname_with_hash(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__varname_with_hash(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mkline := t.NewMkLine("Makefile", 123, "VARNAME.#=\tvalue") |
mkline := t.NewMkLine("Makefile", 123, "VARNAME.#=\tvalue") |
Line 208 func (s *Suite) Test_NewMkLine__varname_ |
|
Line 211 func (s *Suite) Test_NewMkLine__varname_ |
|
// |
// |
// To check that bmake parses them the same, set a breakpoint after the t.NewMkLines |
// To check that bmake parses them the same, set a breakpoint after the t.NewMkLines |
// and look in t.tmpdir for the location of the file. Then run bmake with that file. |
// and look in t.tmpdir for the location of the file. Then run bmake with that file. |
func (s *Suite) Test_NewMkLine__escaped_hash_in_value(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__escaped_hash_in_value(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mklines := t.SetUpFileMkLines("Makefile", |
mklines := t.SetUpFileMkLines("Makefile", |
Line 278 func (s *Suite) Test_VarUseContext_Strin |
|
Line 281 func (s *Suite) Test_VarUseContext_Strin |
|
vartype := G.Pkgsrc.VariableType(nil, "PKGNAME") |
vartype := G.Pkgsrc.VariableType(nil, "PKGNAME") |
vuc := VarUseContext{vartype, vucTimeUnknown, VucQuotBackt, false} |
vuc := VarUseContext{vartype, vucTimeUnknown, VucQuotBackt, false} |
|
|
c.Check(vuc.String(), equals, "(Pkgname time:unknown quoting:backt wordpart:false)") |
c.Check(vuc.String(), equals, "(Pkgname (package-settable) time:unknown quoting:backt wordpart:false)") |
} |
} |
|
|
// In variable assignments, a plain '#' introduces a line comment, unless |
// In variable assignments, a plain '#' introduces a line comment, unless |
// it is escaped by a backslash. In shell commands, on the other hand, it |
// it is escaped by a backslash. In shell commands, on the other hand, it |
// is interpreted literally. |
// is interpreted literally. |
func (s *Suite) Test_NewMkLine__number_sign(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__number_sign(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mklineVarassignEscaped := t.NewMkLine("filename.mk", 1, "SED_CMD=\t's,\\#,hash,g'") |
mklineVarassignEscaped := t.NewMkLine("filename.mk", 1, "SED_CMD=\t's,\\#,hash,g'") |
Line 309 func (s *Suite) Test_NewMkLine__number_s |
|
Line 312 func (s *Suite) Test_NewMkLine__number_s |
|
"WARN: filename.mk:1: The # character starts a Makefile comment.") |
"WARN: filename.mk:1: The # character starts a Makefile comment.") |
} |
} |
|
|
func (s *Suite) Test_NewMkLine__varassign_leading_space(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__varassign_leading_space(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
_ = t.NewMkLine("rubyversion.mk", 427, " _RUBYVER=\t2.15") |
_ = t.NewMkLine("rubyversion.mk", 427, " _RUBYVER=\t2.15") |
Line 327 func (s *Suite) Test_NewMkLine__varassig |
|
Line 330 func (s *Suite) Test_NewMkLine__varassig |
|
// be able to parse and check the infrastructure files as well. |
// be able to parse and check the infrastructure files as well. |
// |
// |
// See Pkgsrc.loadUntypedVars. |
// See Pkgsrc.loadUntypedVars. |
func (s *Suite) Test_NewMkLine__infrastructure(c *check.C) { |
func (s *Suite) Test_MkLineParser_Parse__infrastructure(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
mklines := t.NewMkLines("infra.mk", |
mklines := t.NewMkLines("infra.mk", |
Line 413 func (s *Suite) Test_MkLine_VariableNeed |
|
Line 416 func (s *Suite) Test_MkLine_VariableNeed |
|
MkLineChecker{nil, mkline}.checkVarassign() |
MkLineChecker{nil, mkline}.checkVarassign() |
|
|
t.CheckOutputLines( |
t.CheckOutputLines( |
"WARN: builtin.mk:3: PKG_ADMIN should not be used at load time in any file.", |
|
"NOTE: builtin.mk:3: The :Q operator isn't necessary for ${BUILTIN_PKG.Xfixes} here.") |
"NOTE: builtin.mk:3: The :Q operator isn't necessary for ${BUILTIN_PKG.Xfixes} here.") |
} |
} |
|
|
Line 841 func (s *Suite) Test_MkLine_VariableNeed |
|
Line 843 func (s *Suite) Test_MkLine_VariableNeed |
|
"\tname in which the variable is used or defined. The rules for PATH", |
"\tname in which the variable is used or defined. The rules for PATH", |
"\tare:", |
"\tare:", |
"", |
"", |
"\t* in buildlink3.mk, it should not be accessed at all", |
"\t* in any file, it may be used at load time, or used", |
"\t* in any file, it may be used", |
|
"", |
"", |
"\tIf these rules seem to be incorrect, please ask on the", |
"\tIf these rules seem to be incorrect, please ask on the", |
"\ttech-pkg@NetBSD.org mailing list.", |
"\ttech-pkg@NetBSD.org mailing list.", |
Line 867 func (s *Suite) Test_MkLine_VariableNeed |
|
Line 868 func (s *Suite) Test_MkLine_VariableNeed |
|
"", |
"", |
"\tIf these rules seem to be incorrect, please ask on the", |
"\tIf these rules seem to be incorrect, please ask on the", |
"\ttech-pkg@NetBSD.org mailing list.", |
"\ttech-pkg@NetBSD.org mailing list.", |
"", |
|
"WARN: ~/Makefile:6: PATH should not be used at load time in any file.", |
|
"", |
|
"\tMany variables, especially lists of something, get their values", |
|
"\tincrementally. Therefore it is generally unsafe to rely on their", |
|
"\tvalue until it is clear that it will never change again. This point", |
|
"\tis reached when the whole package Makefile is loaded and execution", |
|
"\tof the shell commands starts; in some cases earlier.", |
|
"", |
|
"\tAdditionally, when using the \":=\" operator, each $$ is replaced with", |
|
"\ta single $, so variables that have references to shell variables or", |
|
"\tregular expressions are modified in a subtle way.", |
|
"", |
|
"\tThe allowed actions for a variable are determined based on the file", |
|
"\tname in which the variable is used or defined. The rules for PATH", |
|
"\tare:", |
|
"", |
|
"\t* in buildlink3.mk, it should not be accessed at all", |
|
"\t* in any file, it may be used", |
|
"", |
|
"\tIf these rules seem to be incorrect, please ask on the", |
|
"\ttech-pkg@NetBSD.org mailing list.", |
|
"") |
"") |
|
|
// Just for branch coverage. |
// Just for branch coverage. |
Line 1142 func (s *Suite) Test_MkLine_ValueFields_ |
|
Line 1121 func (s *Suite) Test_MkLine_ValueFields_ |
|
func (s *Suite) Test_MkLine_ValueTokens(c *check.C) { |
func (s *Suite) Test_MkLine_ValueTokens(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
testTokens := func(value string, expected ...*MkToken) { |
text := func(text string) *MkToken { return &MkToken{text, nil} } |
|
varUseText := func(text string, varname string, modifiers ...string) *MkToken { |
|
return &MkToken{text, NewMkVarUse(varname, modifiers...)} |
|
} |
|
tokens := func(tokens ...*MkToken) []*MkToken { return tokens } |
|
test := func(value string, expected []*MkToken, diagnostics ...string) { |
mkline := t.NewMkLine("Makefile", 1, "PATH=\t"+value) |
mkline := t.NewMkLine("Makefile", 1, "PATH=\t"+value) |
tokens, _ := mkline.ValueTokens() |
actualTokens, _ := mkline.ValueTokens() |
c.Check(tokens, deepEquals, expected) |
c.Check(actualTokens, deepEquals, expected) |
|
t.CheckOutput(diagnostics) |
} |
} |
|
|
testTokens("#empty", |
t.Use(text, varUseText, tokens, test) |
[]*MkToken(nil)...) |
|
|
|
testTokens("value", |
test("#empty", |
&MkToken{"value", nil}) |
tokens()) |
|
|
testTokens("value ${VAR} rest", |
test("value", |
&MkToken{"value ", nil}, |
tokens(text("value"))) |
&MkToken{"${VAR}", NewMkVarUse("VAR")}, |
|
&MkToken{" rest", nil}) |
|
|
|
testTokens("value ${UNFINISHED", |
test("value ${VAR} rest", |
&MkToken{"value ", nil}) |
tokens( |
|
text("value "), |
|
varUseText("${VAR}", "VAR"), |
|
text(" rest"))) |
|
|
|
test("value # comment", |
|
tokens( |
|
text("value"))) |
|
|
|
test("value ${UNFINISHED", |
|
tokens( |
|
text("value "), |
|
varUseText("${UNFINISHED", "UNFINISHED")), |
|
|
|
"WARN: Makefile:1: Missing closing \"}\" for \"UNFINISHED\".") |
} |
} |
|
|
func (s *Suite) Test_MkLine_ValueTokens__caching(c *check.C) { |
func (s *Suite) Test_MkLine_ValueTokens__caching(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
|
tokens := func(tokens ...*MkToken) []*MkToken { return tokens } |
|
|
mkline := t.NewMkLine("Makefile", 1, "PATH=\tvalue ${UNFINISHED") |
mkline := t.NewMkLine("Makefile", 1, "PATH=\tvalue ${UNFINISHED") |
tokens, rest := mkline.ValueTokens() |
valueTokens, rest := mkline.ValueTokens() |
|
|
c.Check(tokens, deepEquals, []*MkToken{{"value ", nil}}) |
c.Check(valueTokens, deepEquals, |
c.Check(rest, equals, "${UNFINISHED") |
tokens( |
|
&MkToken{"value ", nil}, |
|
&MkToken{"${UNFINISHED", NewMkVarUse("UNFINISHED")})) |
|
c.Check(rest, equals, "") |
|
t.CheckOutputLines( |
|
"WARN: Makefile:1: Missing closing \"}\" for \"UNFINISHED\".") |
|
|
tokens2, rest2 := mkline.ValueTokens() // This time the slice is taken from the cache. |
// This time the slice is taken from the cache. |
|
tokens2, rest2 := mkline.ValueTokens() |
|
|
// In Go, it's not possible to compare slices for reference equality. |
c.Check(&tokens2[0], equals, &valueTokens[0]) |
c.Check(tokens2, deepEquals, tokens) |
|
c.Check(rest2, equals, rest) |
c.Check(rest2, equals, rest) |
} |
} |
|
|
func (s *Suite) Test_MkLine_ValueTokens__caching_parse_error(c *check.C) { |
func (s *Suite) Test_MkLine_ValueTokens__caching_parse_error(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
|
tokens := func(tokens ...*MkToken) []*MkToken { return tokens } |
|
varuseText := func(text, varname string, modifiers ...string) *MkToken { |
|
return &MkToken{Text: text, Varuse: NewMkVarUse(varname, modifiers...)} |
|
} |
|
|
mkline := t.NewMkLine("Makefile", 1, "PATH=\t${UNFINISHED") |
mkline := t.NewMkLine("Makefile", 1, "PATH=\t${UNFINISHED") |
tokens, rest := mkline.ValueTokens() |
valueTokens, rest := mkline.ValueTokens() |
|
|
c.Check(tokens, check.IsNil) |
c.Check(valueTokens, deepEquals, tokens(varuseText("${UNFINISHED", "UNFINISHED"))) |
c.Check(rest, equals, "${UNFINISHED") |
c.Check(rest, equals, "") |
|
t.CheckOutputLines( |
|
"WARN: Makefile:1: Missing closing \"}\" for \"UNFINISHED\".") |
|
|
tokens2, rest2 := mkline.ValueTokens() // This time the slice is taken from the cache. |
// This time the slice is taken from the cache. |
|
tokens2, rest2 := mkline.ValueTokens() |
|
|
// In Go, it's not possible to compare slices for reference equality. |
c.Check(&tokens2[0], equals, &valueTokens[0]) |
c.Check(tokens2, deepEquals, tokens) |
|
c.Check(rest2, equals, rest) |
c.Check(rest2, equals, rest) |
} |
} |
|
|
Line 1266 func (s *Suite) Test_MkLine_ResolveVarsI |
|
Line 1276 func (s *Suite) Test_MkLine_ResolveVarsI |
|
"WARN: ~/multimedia/totem/bla.mk:2: "+ |
"WARN: ~/multimedia/totem/bla.mk:2: "+ |
"The variable BUILDLINK_PKGSRCDIR.totem should not be given a default value in this file; "+ |
"The variable BUILDLINK_PKGSRCDIR.totem should not be given a default value in this file; "+ |
"it would be ok in buildlink3.mk.", |
"it would be ok in buildlink3.mk.", |
"ERROR: ~/multimedia/totem/bla.mk:2: There is no package in \"multimedia/totem\".") |
"ERROR: ~/multimedia/totem/bla.mk:2: Relative path \"../../multimedia/totem/Makefile\" does not exist.") |
} |
} |
|
|
func (s *Suite) Test_MatchVarassign(c *check.C) { |
func (s *Suite) Test_MkLineParser_MatchVarassign(c *check.C) { |
s.Init(c) |
t := s.Init(c) |
|
|
test := func(text string, commented bool, varname, spaceAfterVarname, op, align, value, spaceAfterValue, comment string) { |
test := func(text string, commented bool, varname, spaceAfterVarname, op, align, value, spaceAfterValue, comment string, diagnostics ...string) { |
m, actual := MatchVarassign(text) |
line := t.NewLine("filename.mk", 123, text) |
|
data := MkLineParser{}.split(line, text) |
|
m, actual := MkLineParser{}.MatchVarassign(line, text, data) |
if !m { |
if !m { |
c.Errorf("Text %q doesn't match variable assignment", text) |
c.Errorf("Text %q doesn't match variable assignment", text) |
return |
return |
Line 1295 func (s *Suite) Test_MatchVarassign(c *c |
|
Line 1307 func (s *Suite) Test_MatchVarassign(c *c |
|
comment: comment, |
comment: comment, |
} |
} |
c.Check(*actual, deepEquals, expected) |
c.Check(*actual, deepEquals, expected) |
|
t.CheckOutput(diagnostics) |
} |
} |
|
|
testInvalid := func(text string) { |
testInvalid := func(text string, diagnostics ...string) { |
m, _ := MatchVarassign(text) |
line := t.NewLine("filename.mk", 123, text) |
|
data := MkLineParser{}.split(nil, text) |
|
m, _ := MkLineParser{}.MatchVarassign(line, text, data) |
if m { |
if m { |
c.Errorf("Text %q matches variable assignment but shouldn't.", text) |
c.Errorf("Text %q matches variable assignment but shouldn't.", text) |
} |
} |
|
t.CheckOutput(diagnostics) |
} |
} |
|
|
test("C++=c11", false, "C+", "", "+=", "C++=", "c11", "", "") |
test("C++=c11", false, "C+", "", "+=", "C++=", "c11", "", "") |
Line 1396 func (s *Suite) Test_MatchVarassign(c *c |
|
Line 1412 func (s *Suite) Test_MatchVarassign(c *c |
|
"# none") |
"# none") |
|
|
test("EGDIRS=\t${EGDIR/apparmor.d ${EGDIR/dbus-1/system.d ${EGDIR/pam.d", |
test("EGDIRS=\t${EGDIR/apparmor.d ${EGDIR/dbus-1/system.d ${EGDIR/pam.d", |
|
|
false, |
false, |
"EGDIRS", |
"EGDIRS", |
"", |
"", |
Line 1403 func (s *Suite) Test_MatchVarassign(c *c |
|
Line 1420 func (s *Suite) Test_MatchVarassign(c *c |
|
"EGDIRS=\t", |
"EGDIRS=\t", |
"${EGDIR/apparmor.d ${EGDIR/dbus-1/system.d ${EGDIR/pam.d", |
"${EGDIR/apparmor.d ${EGDIR/dbus-1/system.d ${EGDIR/pam.d", |
"", |
"", |
"") |
"", |
|
|
|
"WARN: filename.mk:123: Missing closing \"}\" for \"EGDIR/pam.d\".", |
|
"WARN: filename.mk:123: Invalid part \"/pam.d\" after variable name \"EGDIR\".", |
|
"WARN: filename.mk:123: Missing closing \"}\" for \"EGDIR/dbus-1/system.d ${EGDIR/pam.d\".", |
|
"WARN: filename.mk:123: Invalid part \"/dbus-1/system.d ${EGDIR/pam.d\" after variable name \"EGDIR\".", |
|
"WARN: filename.mk:123: Missing closing \"}\" for \"EGDIR/apparmor.d ${EGDIR/dbus-1/system.d ${EGDIR/pam.d\".", |
|
"WARN: filename.mk:123: Invalid part \"/apparmor.d ${EGDIR/dbus-1/system.d ${EGDIR/pam.d\" after variable name \"EGDIR\".") |
|
|
test("VAR:=\t${VAR:M-*:[\\#]}", |
test("VAR:=\t${VAR:M-*:[\\#]}", |
false, |
false, |
Line 1414 func (s *Suite) Test_MatchVarassign(c *c |
|
Line 1438 func (s *Suite) Test_MatchVarassign(c *c |
|
"${VAR:M-*:[#]}", |
"${VAR:M-*:[#]}", |
"", |
"", |
"") |
"") |
|
|
|
test("#VAR=value", |
|
true, "VAR", "", "=", "#VAR=", "value", "", "") |
|
|
|
testInvalid("# VAR=value") |
|
testInvalid("#\tVAR=value") |
|
testInvalid(MkRcsID) |
} |
} |
|
|
func (s *Suite) Test_NewMkOperator(c *check.C) { |
func (s *Suite) Test_NewMkOperator(c *check.C) { |
Line 1532 func (s *Suite) Test_Indentation_Varname |
|
Line 1563 func (s *Suite) Test_Indentation_Varname |
|
". include \"../../category/other/buildlink3.mk\"", |
". include \"../../category/other/buildlink3.mk\"", |
". endif", |
". endif", |
".endif") |
".endif") |
|
t.FinishSetUp() |
|
|
G.Check(t.File("category/package")) |
G.Check(t.File("category/package")) |
|
|
Line 1590 func (s *Suite) Test_MkLine_ForEachUsed( |
|
Line 1622 func (s *Suite) Test_MkLine_ForEachUsed( |
|
"run <", |
"run <", |
"run @", |
"run @", |
"run x"}) |
"run x"}) |
|
t.CheckOutputLines( |
|
"WARN: Makefile:12: Please use curly braces {} instead of round parentheses () for ROUND_PARENTHESES.", |
|
"WARN: Makefile:14: $x is ambiguous. Use ${x} if you mean a Make variable or $$x if you mean a shell variable.") |
} |
} |
|
|
func (s *Suite) Test_MkLine_UnquoteShell(c *check.C) { |
func (s *Suite) Test_MkLine_UnquoteShell(c *check.C) { |
Line 1621 func (s *Suite) Test_MkLine_UnquoteShell |
|
Line 1656 func (s *Suite) Test_MkLine_UnquoteShell |
|
test("`", "`") |
test("`", "`") |
} |
} |
|
|
func (s *Suite) Test_unescapeMkComment(c *check.C) { |
func (s *Suite) Test_MkLineParser_unescapeComment(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
test := func(text string, main, comment string) { |
test := func(text string, main, comment string) { |
aMain, aComment := unescapeMkComment(text) |
aMain, aComment := MkLineParser{}.unescapeComment(text) |
t.Check( |
t.Check( |
[]interface{}{text, aMain, aComment}, |
[]interface{}{text, aMain, aComment}, |
deepEquals, |
deepEquals, |
Line 1741 func (s *Suite) Test_unescapeMkComment(c |
|
Line 1776 func (s *Suite) Test_unescapeMkComment(c |
|
"#comment") |
"#comment") |
} |
} |
|
|
func (s *Suite) Test_splitMkLine(c *check.C) { |
func (s *Suite) Test_MkLineParser_split(c *check.C) { |
t := s.Init(c) |
t := s.Init(c) |
|
|
varuse := func(varname string, modifiers ...string) *MkToken { |
varuse := func(varname string, modifiers ...string) *MkToken { |
text := "${" + varname |
var text strings.Builder |
|
text.WriteString("${") |
|
text.WriteString(varname) |
for _, modifier := range modifiers { |
for _, modifier := range modifiers { |
text += ":" + modifier |
text.WriteString(":") |
|
text.WriteString(modifier) |
} |
} |
text += "}" |
text.WriteString("}") |
return &MkToken{Text: text, Varuse: NewMkVarUse(varname, modifiers...)} |
return &MkToken{Text: text.String(), Varuse: NewMkVarUse(varname, modifiers...)} |
} |
} |
varuseText := func(text, varname string, modifiers ...string) *MkToken { |
varuseText := func(text, varname string, modifiers ...string) *MkToken { |
return &MkToken{Text: text, Varuse: NewMkVarUse(varname, modifiers...)} |
return &MkToken{Text: text, Varuse: NewMkVarUse(varname, modifiers...)} |
Line 1761 func (s *Suite) Test_splitMkLine(c *chec |
|
Line 1799 func (s *Suite) Test_splitMkLine(c *chec |
|
tokens := func(tokens ...*MkToken) []*MkToken { |
tokens := func(tokens ...*MkToken) []*MkToken { |
return tokens |
return tokens |
} |
} |
_, _, _, _ = text, varuse, varuseText, tokens |
|
|
|
test := func(text string, main string, tokens []*MkToken, rest string, spaceBeforeComment string, hasComment bool, comment string) { |
test := func(text string, data mkLineSplitResult, diagnostics ...string) { |
aMain, aTokens, aRest, aSpaceBeforeComment, aHasComment, aComment := splitMkLine(text) |
line := t.NewLine("filename.mk", 123, text) |
t.Check( |
actualData := MkLineParser{}.split(line, text) |
[]interface{}{text, aTokens, aMain, aRest, aSpaceBeforeComment, aHasComment, aComment}, |
|
deepEquals, |
t.CheckOutput(diagnostics) |
[]interface{}{text, tokens, main, rest, spaceBeforeComment, hasComment, comment}) |
t.Check([]interface{}{text, actualData}, deepEquals, []interface{}{text, data}) |
} |
} |
|
|
test("", |
t.Use(text, varuse, varuseText, tokens) |
"", |
|
tokens(), |
test( |
"", |
|
"", |
"", |
false, |
mkLineSplitResult{}) |
"") |
|
test("text", |
test( |
"text", |
"text", |
tokens(text("text")), |
mkLineSplitResult{ |
"", |
main: "text", |
"", |
tokens: tokens(text("text")), |
false, |
}) |
"") |
|
|
// Leading space is always kept. |
|
test( |
|
" text", |
|
mkLineSplitResult{ |
|
main: " text", |
|
tokens: tokens(text(" text")), |
|
}) |
|
|
|
// Trailing space does not end up in the tokens since it is usually |
|
// ignored. |
|
test( |
|
"text\t", |
|
mkLineSplitResult{ |
|
main: "text", |
|
tokens: tokens(text("text")), |
|
spaceBeforeComment: "\t", |
|
}) |
|
|
|
test( |
|
"text\t# intended comment", |
|
mkLineSplitResult{ |
|
main: "text", |
|
tokens: tokens(text("text")), |
|
spaceBeforeComment: "\t", |
|
hasComment: true, |
|
comment: " intended comment", |
|
}) |
|
|
|
// Trailing space is saved in a separate field to detect accidental |
|
// unescaped # in the middle of a word, like the URL fragment in this |
|
// example. |
|
test( |
|
"url#fragment", |
|
mkLineSplitResult{ |
|
main: "url", |
|
tokens: tokens(text("url")), |
|
hasComment: true, |
|
comment: "fragment", |
|
}) |
|
|
// The leading space from the comment is preserved to make parsing as exact |
// The leading space from the comment is preserved to make parsing as exact |
// as possible. |
// as possible. |
Line 1792 func (s *Suite) Test_splitMkLine(c *chec |
|
Line 1867 func (s *Suite) Test_splitMkLine(c *chec |
|
// The difference between "#defined" and "# defined" is relevant in a few |
// The difference between "#defined" and "# defined" is relevant in a few |
// cases, such as the API documentation of the infrastructure files. |
// cases, such as the API documentation of the infrastructure files. |
test("# comment", |
test("# comment", |
"", |
mkLineSplitResult{ |
tokens(), |
hasComment: true, |
"", |
comment: " comment", |
"", |
}) |
true, |
|
" comment") |
|
test("#\tcomment", |
test("#\tcomment", |
"", |
mkLineSplitResult{ |
tokens(), |
hasComment: true, |
"", |
comment: "\tcomment", |
"", |
}) |
true, |
|
"\tcomment") |
|
test("# comment", |
test("# comment", |
"", |
mkLineSplitResult{ |
tokens(), |
hasComment: true, |
"", |
comment: " comment", |
"", |
}) |
true, |
|
" comment") |
|
|
|
// Other than in the shell, # also starts a comment in the middle of a word. |
// Other than in the shell, # also starts a comment in the middle of a word. |
test("COMMENT=\tThe C# compiler", |
test("COMMENT=\tThe C# compiler", |
"COMMENT=\tThe C", |
mkLineSplitResult{ |
tokens(text("COMMENT=\tThe C")), |
main: "COMMENT=\tThe C", |
"", |
tokens: tokens(text("COMMENT=\tThe C")), |
"", |
hasComment: true, |
true, |
comment: " compiler", |
" compiler") |
}) |
|
|
test("COMMENT=\tThe C\\# compiler", |
test("COMMENT=\tThe C\\# compiler", |
"COMMENT=\tThe C# compiler", |
mkLineSplitResult{ |
tokens(text("COMMENT=\tThe C# compiler")), |
main: "COMMENT=\tThe C# compiler", |
"", |
tokens: tokens(text("COMMENT=\tThe C# compiler")), |
"", |
hasComment: false, |
false, |
comment: "", |
"") |
}) |
|
|
test("${TARGET}: ${SOURCES} # comment", |
test("${TARGET}: ${SOURCES} # comment", |
"${TARGET}: ${SOURCES}", |
mkLineSplitResult{ |
tokens(varuse("TARGET"), text(": "), varuse("SOURCES"), text(" ")), |
main: "${TARGET}: ${SOURCES}", |
"", |
tokens: tokens(varuse("TARGET"), text(": "), varuse("SOURCES")), |
" ", |
spaceBeforeComment: " ", |
true, |
hasComment: true, |
" comment") |
comment: " comment", |
|
}) |
|
|
// A # starts a comment, except if it immediately follows a [. |
// A # starts a comment, except if it immediately follows a [. |
// This is done so that the length modifier :[#] can be written without |
// This is done so that the length modifier :[#] can be written without |
// escaping the #. |
// escaping the #. |
test("VAR=\t${OTHER:[#]} # comment", |
test("VAR=\t${OTHER:[#]} # comment", |
"VAR=\t${OTHER:[#]}", |
mkLineSplitResult{ |
tokens(text("VAR=\t"), varuse("OTHER", "[#]"), text(" ")), |
main: "VAR=\t${OTHER:[#]}", |
"", |
tokens: tokens(text("VAR=\t"), varuse("OTHER", "[#]")), |
" ", |
spaceBeforeComment: " ", |
true, |
hasComment: true, |
" comment") |
comment: " comment", |
|
}) |
|
|
// The # in the :[#] modifier may be escaped or not. Both forms are equivalent. |
// The # in the :[#] modifier may be escaped or not. Both forms are equivalent. |
test("VAR:=\t${VAR:M-*:[\\#]}", |
test("VAR:=\t${VAR:M-*:[\\#]}", |
"VAR:=\t${VAR:M-*:[#]}", |
mkLineSplitResult{ |
tokens(text("VAR:=\t"), varuse("VAR", "M-*", "[#]")), |
main: "VAR:=\t${VAR:M-*:[#]}", |
"", |
tokens: tokens(text("VAR:=\t"), varuse("VAR", "M-*", "[#]")), |
"", |
}) |
false, |
|
"") |
|
|
|
// A backslash always escapes the next character, be it a # for a comment |
// A backslash always escapes the next character, be it a # for a comment |
// or something else. This makes it difficult to write a literal \# in a |
// or something else. This makes it difficult to write a literal \# in a |
// Makefile, but that's an edge case anyway. |
// Makefile, but that's an edge case anyway. |
test("VAR0=\t#comment", |
test("VAR0=\t#comment", |
"VAR0=", |
mkLineSplitResult{ |
tokens(text("VAR0=\t")), |
main: "VAR0=", |
"", |
tokens: tokens(text("VAR0=")), |
// Later, when converting this result into a proper variable assignment, |
// Later, when converting this result into a proper variable assignment, |
// this "space before comment" is reclassified as "space before the value", |
// this "space before comment" is reclassified as "space before the value", |
// in order to align the "#comment" with the other variable values. |
// in order to align the "#comment" with the other variable values. |
"\t", |
spaceBeforeComment: "\t", |
true, |
hasComment: true, |
"comment") |
comment: "comment", |
|
}) |
|
|
test("VAR1=\t\\#no-comment", |
test("VAR1=\t\\#no-comment", |
"VAR1=\t#no-comment", |
mkLineSplitResult{ |
tokens(text("VAR1=\t#no-comment")), |
main: "VAR1=\t#no-comment", |
"", |
tokens: tokens(text("VAR1=\t#no-comment")), |
"", |
}) |
false, |
|
"") |
|
test("VAR2=\t\\\\#comment", |
test("VAR2=\t\\\\#comment", |
"VAR2=\t\\\\", |
mkLineSplitResult{ |
tokens(text("VAR2=\t\\\\")), |
main: "VAR2=\t\\\\", |
"", |
tokens: tokens(text("VAR2=\t\\\\")), |
"", |
hasComment: true, |
true, |
comment: "comment", |
"comment") |
}) |
|
|
// The backslash is only removed when it escapes a comment. |
// The backslash is only removed when it escapes a comment. |
// In particular, it cannot be used to escape a dollar that starts a |
// In particular, it cannot be used to escape a dollar that starts a |
// variable use. |
// variable use. |
test("VAR0=\t$T", |
test("VAR0=\t$T", |
"VAR0=\t$T", |
mkLineSplitResult{ |
tokens(text("VAR0=\t"), varuseText("$T", "T")), |
main: "VAR0=\t$T", |
"", |
tokens: tokens(text("VAR0=\t"), varuseText("$T", "T")), |
"", |
}, |
false, |
"WARN: filename.mk:123: $T is ambiguous. Use ${T} if you mean a Make variable or $$T if you mean a shell variable.") |
"") |
|
test("VAR1=\t\\$T", |
test("VAR1=\t\\$T", |
"VAR1=\t\\$T", |
mkLineSplitResult{ |
tokens(text("VAR1=\t\\"), varuseText("$T", "T")), |
main: "VAR1=\t\\$T", |
"", |
tokens: tokens(text("VAR1=\t\\"), varuseText("$T", "T")), |
"", |
}, |
false, |
"WARN: filename.mk:123: $T is ambiguous. Use ${T} if you mean a Make variable or $$T if you mean a shell variable.") |
"") |
|
test("VAR2=\t\\\\$T", |
test("VAR2=\t\\\\$T", |
"VAR2=\t\\\\$T", |
mkLineSplitResult{ |
tokens(text("VAR2=\t\\\\"), varuseText("$T", "T")), |
main: "VAR2=\t\\\\$T", |
"", |
tokens: tokens(text("VAR2=\t\\\\"), varuseText("$T", "T")), |
"", |
}, |
false, |
"WARN: filename.mk:123: $T is ambiguous. Use ${T} if you mean a Make variable or $$T if you mean a shell variable.") |
"") |
|
|
|
// To escape a dollar, write it twice. |
// To escape a dollar, write it twice. |
test("$$shellvar $${shellvar} \\${MKVAR} [] \\x", |
test("$$shellvar $${shellvar} \\${MKVAR} [] \\x", |
"$$shellvar $${shellvar} \\${MKVAR} [] \\x", |
mkLineSplitResult{ |
tokens(text("$$shellvar $${shellvar} \\"), varuse("MKVAR"), text(" [] \\x")), |
main: "$$shellvar $${shellvar} \\${MKVAR} [] \\x", |
"", |
tokens: tokens(text("$$shellvar $${shellvar} \\"), varuse("MKVAR"), text(" [] \\x")), |
"", |
}) |
false, |
|
"") |
|
|
|
// Parse errors are recorded in the rest return value. |
// Parse errors are recorded in the rest return value. |
test("${UNCLOSED", |
test("${UNCLOSED", |
"", |
mkLineSplitResult{ |
tokens(), |
main: "${UNCLOSED", |
"${UNCLOSED", |
tokens: tokens(varuseText("${UNCLOSED", "UNCLOSED")), |
"", |
}, |
false, |
"WARN: filename.mk:123: Missing closing \"}\" for \"UNCLOSED\".") |
"") |
|
|
|
// Even if there is a parse error in the main part, |
// Even if there is a parse error in the main part, |
// the comment is extracted. |
// the comment is extracted. |
test("text before ${UNCLOSED# comment", |
test("text before ${UNCLOSED# comment", |
"text before ", |
mkLineSplitResult{ |
tokens(text("text before ")), |
main: "text before ${UNCLOSED", |
"${UNCLOSED", |
tokens: tokens( |
"", |
text("text before "), |
true, |
varuseText("${UNCLOSED", "UNCLOSED")), |
" comment") |
hasComment: true, |
|
comment: " comment", |
|
}, |
|
"WARN: filename.mk:123: Missing closing \"}\" for \"UNCLOSED\".") |
|
|
// Even in case of parse errors, the space before the comment is parsed |
// Even in case of parse errors, the space before the comment is parsed |
// correctly. |
// correctly. |
test("text before ${UNCLOSED # comment", |
test("text before ${UNCLOSED # comment", |
"text before ", |
mkLineSplitResult{ |
tokens(text("text before ")), |
main: "text before ${UNCLOSED", |
"${UNCLOSED", |
tokens: tokens( |
" ", |
text("text before "), |
true, |
// It's a bit inconsistent that the varname includes the space |
" comment") |
// but the text doesn't; anyway, it's an edge case. |
|
varuseText("${UNCLOSED", "UNCLOSED ")), |
|
spaceBeforeComment: " ", |
|
hasComment: true, |
|
comment: " comment", |
|
}, |
|
"WARN: filename.mk:123: Missing closing \"}\" for \"UNCLOSED \".", |
|
"WARN: filename.mk:123: Invalid part \" \" after variable name \"UNCLOSED\".") |
|
|
// The dollar-space refers to a normal Make variable named " ". |
// The dollar-space refers to a normal Make variable named " ". |
// The lonely dollar at the very end refers to the variable named "", |
// The lonely dollar at the very end refers to the variable named "", |
Line 1957 func (s *Suite) Test_splitMkLine(c *chec |
|
Line 2036 func (s *Suite) Test_splitMkLine(c *chec |
|
// variable name, mainly because the empty variable name is not visible |
// variable name, mainly because the empty variable name is not visible |
// outside of the bmake debugging mode. |
// outside of the bmake debugging mode. |
test("Lonely $ character $", |
test("Lonely $ character $", |
"Lonely $ character ", |
mkLineSplitResult{ |
tokens( |
main: "Lonely $ character $", |
text("Lonely "), |
tokens: tokens( |
varuseText("$ " /* instead of "${ }" */, " "), |
text("Lonely "), |
text("character ")), |
varuseText("$ " /* instead of "${ }" */, " "), |
"$", |
text("character "), |
"", |
text("$")), |
false, |
}) |
"") |
|
|
|
// The character [ prevents the following # from starting a comment, even |
// The character [ prevents the following # from starting a comment, even |
// outside of variable modifiers. |
// outside of variable modifiers. |
test("COMMENT=\t[#] $$\\# $$# comment", |
test("COMMENT=\t[#] $$\\# $$# comment", |
"COMMENT=\t[#] $$# $$", |
mkLineSplitResult{ |
tokens(text("COMMENT=\t[#] $$# $$")), |
main: "COMMENT=\t[#] $$# $$", |
"", |
tokens: tokens(text("COMMENT=\t[#] $$# $$")), |
"", |
hasComment: true, |
true, |
comment: " comment", |
" comment") |
}) |
|
|
test("VAR2=\t\\\\#comment", |
test("VAR2=\t\\\\#comment", |
"VAR2=\t\\\\", |
mkLineSplitResult{ |
tokens(text("VAR2=\t\\\\")), |
main: "VAR2=\t\\\\", |
"", |
tokens: tokens(text("VAR2=\t\\\\")), |
"", |
hasComment: true, |
true, |
comment: "comment", |
"comment") |
}) |
|
|
|
// At this stage, MkLine.split doesn't know that empty(...) takes |
|
// a variable use. Instead it just sees ordinary characters and |
|
// other uses of variables. |
|
test(".if empty(${VAR.${tool}}:C/\\:.*$//:M${pattern})", |
|
mkLineSplitResult{ |
|
main: ".if empty(${VAR.${tool}}:C/\\:.*$//:M${pattern})", |
|
tokens: tokens( |
|
text(".if empty("), |
|
varuse("VAR.${tool}"), |
|
text(":C/\\:.*"), |
|
text("$"), |
|
text("//:M"), |
|
varuse("pattern"), |
|
text(")")), |
|
}) |
} |
} |
|
|
func (s *Suite) Test_matchMkDirective(c *check.C) { |
func (s *Suite) Test_MkLineParser_parseDirective(c *check.C) { |
|
t := s.Init(c) |
|
|
|
test := func(input, expectedIndent, expectedDirective, expectedArgs, expectedComment string, diagnostics ...string) { |
|
line := t.NewLine("filename.mk", 123, input) |
|
data := MkLineParser{}.split(line, input) |
|
mkline := MkLineParser{}.parseDirective(line, data) |
|
if !c.Check(mkline, check.NotNil) { |
|
return |
|
} |
|
|
test := func(input, expectedIndent, expectedDirective, expectedArgs, expectedComment string) { |
|
m, indent, directive, args, comment := matchMkDirective(input) |
|
c.Check( |
c.Check( |
[]interface{}{m, indent, directive, args, comment}, |
[]interface{}{mkline.Indent(), mkline.Directive(), mkline.Args(), mkline.DirectiveComment()}, |
deepEquals, |
deepEquals, |
[]interface{}{true, expectedIndent, expectedDirective, expectedArgs, expectedComment}) |
[]interface{}{expectedIndent, expectedDirective, expectedArgs, expectedComment}) |
} |
t.CheckOutput(diagnostics) |
|
|
testFail := func(input string) { |
|
m, indent, directive, args, comment := matchMkDirective(input) |
|
if m { |
|
c.Errorf("The line %q could be parsed as directive (%q, %q, %q, %q) but shouldn't.", |
|
indent, directive, args, comment) |
|
} |
|
} |
} |
|
|
test(".if ${VAR} == value", |
test(".if ${VAR} == value", |
"", "if", "${VAR} == value", "") |
"", "if", "${VAR} == value", "") |
|
|
test(".\tendif # comment", |
test(".\tendif # comment", |
"\t", "endif", "", " comment") |
"\t", "endif", "", "comment") |
|
|
test(".if ${VAR} == \"#\"", |
test(".if ${VAR} == \"#\"", |
"", "if", "${VAR} == \"", "\"") |
"", "if", "${VAR} == \"", "\"") |
Line 2019 func (s *Suite) Test_matchMkDirective(c |
|
Line 2113 func (s *Suite) Test_matchMkDirective(c |
|
test(".if ${VAR} == \\", |
test(".if ${VAR} == \\", |
"", "if", "${VAR} == \\", "") |
"", "if", "${VAR} == \\", "") |
|
|
// Unclosed variable |
test(".if ${VAR", |
testFail(".if ${VAR") |
"", "if", "${VAR", "", |
|
"WARN: filename.mk:123: Missing closing \"}\" for \"VAR\".") |
} |
} |
|
|
func (s *Suite) Test_MatchMkInclude(c *check.C) { |
func (s *Suite) Test_MatchMkInclude(c *check.C) { |