Updated pkglint to 5.3.2 Changes since 5.3.1: Alignment of variable values is no longer checked by single line, but by the complete block (e.g. SUBST_*). Pkglint now checks that all variables belonging to a block are indented consistently, so that their values are aligned nicely. Since pkglint does not report warnings, but only notes, and since it can fix them automatically, the burden on the package developers will be very low. Especially, since these notes are only printed when pkglint is called with the -Wspace or -Wall options. Also, pkglint supports running its unit tests now.diff -r1.477 -r1.478 pkgsrc/pkgtools/pkglint/Makefile
(rillig)
@@ -1,50 +1,50 @@ | @@ -1,50 +1,50 @@ | |||
1 | # $NetBSD: Makefile,v 1.477 2016/01/18 15:33:44 fhajny Exp $ | 1 | # $NetBSD: Makefile,v 1.478 2016/01/24 02:03:28 rillig Exp $ | |
2 | 2 | |||
3 | PKGNAME= pkglint-5.3.1 | 3 | PKGNAME= pkglint-5.3.2 | |
4 | PKGREVISION= 1 | |||
5 | DISTFILES= # none | 4 | DISTFILES= # none | |
6 | CATEGORIES= pkgtools | 5 | CATEGORIES= pkgtools | |
7 | 6 | |||
8 | OWNER= rillig@NetBSD.org | 7 | OWNER= rillig@NetBSD.org | |
9 | HOMEPAGE= http://www.NetBSD.org/docs/pkgsrc/ | 8 | HOMEPAGE= http://www.NetBSD.org/docs/pkgsrc/ | |
10 | COMMENT= Verifier for NetBSD packages | 9 | COMMENT= Verifier for NetBSD packages | |
11 | LICENSE= 2-clause-bsd | 10 | LICENSE= 2-clause-bsd | |
12 | CONFLICTS+= pkglint4-[0-9]* | 11 | CONFLICTS+= pkglint4-[0-9]* | |
13 | 12 | |||
14 | WRKSRC= ${WRKDIR}/netbsd.org/pkglint | 13 | WRKSRC= ${WRKDIR}/netbsd.org/pkglint | |
15 | NO_CHECKSUM= yes | 14 | NO_CHECKSUM= yes | |
16 | USE_LANGUAGES= # none | 15 | USE_LANGUAGES= # none | |
16 | USE_TOOLS+= pax | |||
17 | AUTO_MKDIRS= yes | 17 | AUTO_MKDIRS= yes | |
18 | GO_SRCPATH= netbsd.org/pkglint | 18 | GO_SRCPATH= netbsd.org/pkglint | |
19 | 19 | |||
20 | SUBST_CLASSES+= pkglint | 20 | SUBST_CLASSES+= pkglint | |
21 | SUBST_STAGE.pkglint= post-configure | 21 | SUBST_STAGE.pkglint= post-configure | |
22 | SUBST_FILES.pkglint+= main.go | 22 | SUBST_FILES.pkglint+= main.go | |
23 | SUBST_SED.pkglint+= -e s\|@VERSION@\|${PKGNAME:S/pkglint-//}\|g | 23 | SUBST_SED.pkglint+= -e s\|@VERSION@\|${PKGNAME:S/pkglint-//}\|g | |
24 | SUBST_SED.pkglint+= -e s\|@BMAKE@\|${MAKE:Q}\|g | 24 | SUBST_SED.pkglint+= -e s\|@BMAKE@\|${MAKE:Q}\|g | |
25 | 25 | |||
26 | do-extract: | 26 | do-extract: | |
27 | mkdir -p ${WRKDIR}/pkglint/plist-clash | 27 | ${RUN} mkdir -p ${WRKDIR}/pkglint/plist-clash | |
28 | cd ${FILESDIR} && ${PAX} -rw *.go */*.go pkglint.[01] ${WRKDIR}/pkglint | 28 | ${RUN} cd ${FILESDIR} && ${PAX} -rw *.go */*.go pkglint.[01] ${WRKDIR}/pkglint | |
29 | ||||
30 | do-test: | |||
31 | cd ${WRKSRC} && go test | |||
32 | 29 | |||
33 | do-install: do-install-man | 30 | do-install: do-install-man | |
34 | 31 | |||
35 | .include "../../mk/bsd.prefs.mk" | 32 | .include "../../mk/bsd.prefs.mk" | |
36 | 33 | |||
37 | do-install-man: | 34 | do-install-man: .PHONY | |
38 | .if !empty(MANINSTALL:Mcatinstall) | 35 | .if !empty(MANINSTALL:Mcatinstall) | |
39 | . if defined(CATMAN_SECTION_SUFFIX) && !empty(CATMAN_SECTION_SUFFIX:M[Yy][Ee][Ss]) | 36 | . if defined(CATMAN_SECTION_SUFFIX) && !empty(CATMAN_SECTION_SUFFIX:M[Yy][Ee][Ss]) | |
40 | ${INSTALL_MAN} ${WRKSRC}/pkglint.0 ${DESTDIR}${PREFIX}/${PKGMANDIR}/cat1/pkglint.1 | 37 | ${INSTALL_MAN} ${WRKSRC}/pkglint.0 ${DESTDIR}${PREFIX}/${PKGMANDIR}/cat1/pkglint.1 | |
41 | . else | 38 | . else | |
42 | ${INSTALL_MAN} ${WRKSRC}/pkglint.0 ${DESTDIR}${PREFIX}/${PKGMANDIR}/cat1 | 39 | ${INSTALL_MAN} ${WRKSRC}/pkglint.0 ${DESTDIR}${PREFIX}/${PKGMANDIR}/cat1 | |
43 | . endif | 40 | . endif | |
44 | .endif | 41 | .endif | |
45 | .if !empty(MANINSTALL:Mmaninstall) | 42 | .if !empty(MANINSTALL:Mmaninstall) | |
46 | ${INSTALL_MAN} ${WRKSRC}/pkglint.1 ${DESTDIR}${PREFIX}/${PKGMANDIR}/man1 | 43 | ${INSTALL_MAN} ${WRKSRC}/pkglint.1 ${DESTDIR}${PREFIX}/${PKGMANDIR}/man1 | |
47 | .endif | 44 | .endif | |
48 | 45 | |||
49 | .include "../../lang/go/go-package.mk" | 46 | .include "../../lang/go/go-package.mk" | |
47 | .if !empty(PKGSRC_RUN_TEST:M[yY][eE][sS]) | |||
48 | .include "../../devel/go-check/buildlink3.mk" | |||
49 | .endif | |||
50 | .include "../../mk/bsd.pkg.mk" | 50 | .include "../../mk/bsd.pkg.mk" |
@@ -107,26 +107,34 @@ func (s *Suite) RegisterTool(toolname, v | @@ -107,26 +107,34 @@ func (s *Suite) RegisterTool(toolname, v | |||
107 | func (s *Suite) CreateTmpFile(c *check.C, relFname, content string) (absFname string) { | 107 | func (s *Suite) CreateTmpFile(c *check.C, relFname, content string) (absFname string) { | |
108 | if s.tmpdir == "" { | 108 | if s.tmpdir == "" { | |
109 | s.tmpdir = filepath.ToSlash(c.MkDir()) | 109 | s.tmpdir = filepath.ToSlash(c.MkDir()) | |
110 | } | 110 | } | |
111 | absFname = s.tmpdir + "/" + relFname | 111 | absFname = s.tmpdir + "/" + relFname | |
112 | err := os.MkdirAll(path.Dir(absFname), 0777) | 112 | err := os.MkdirAll(path.Dir(absFname), 0777) | |
113 | c.Assert(err, check.IsNil) | 113 | c.Assert(err, check.IsNil) | |
114 | 114 | |||
115 | err = ioutil.WriteFile(absFname, []byte(content), 0666) | 115 | err = ioutil.WriteFile(absFname, []byte(content), 0666) | |
116 | c.Check(err, check.IsNil) | 116 | c.Check(err, check.IsNil) | |
117 | return | 117 | return | |
118 | } | 118 | } | |
119 | 119 | |||
120 | func (s *Suite) CreateTmpFileLines(c *check.C, relFname string, rawTexts ...string) (absFname string) { | |||
121 | text := "" | |||
122 | for _, rawText := range rawTexts { | |||
123 | text += rawText + "\n" | |||
124 | } | |||
125 | return s.CreateTmpFile(c, relFname, text) | |||
126 | } | |||
127 | ||||
120 | func (s *Suite) LoadTmpFile(c *check.C, relFname string) string { | 128 | func (s *Suite) LoadTmpFile(c *check.C, relFname string) string { | |
121 | bytes, err := ioutil.ReadFile(s.tmpdir + "/" + relFname) | 129 | bytes, err := ioutil.ReadFile(s.tmpdir + "/" + relFname) | |
122 | c.Assert(err, check.IsNil) | 130 | c.Assert(err, check.IsNil) | |
123 | return string(bytes) | 131 | return string(bytes) | |
124 | } | 132 | } | |
125 | 133 | |||
126 | func (s *Suite) ExpectFatalError(action func()) { | 134 | func (s *Suite) ExpectFatalError(action func()) { | |
127 | if r := recover(); r != nil { | 135 | if r := recover(); r != nil { | |
128 | if _, ok := r.(pkglintFatal); ok { | 136 | if _, ok := r.(pkglintFatal); ok { | |
129 | action() | 137 | action() | |
130 | return | 138 | return | |
131 | } | 139 | } | |
132 | panic(r) | 140 | panic(r) |
@@ -65,27 +65,27 @@ func (gd *GlobalData) Initialize() { | @@ -65,27 +65,27 @@ func (gd *GlobalData) Initialize() { | |||
65 | gd.loadSuggestedUpdates() | 65 | gd.loadSuggestedUpdates() | |
66 | gd.loadUserDefinedVars() | 66 | gd.loadUserDefinedVars() | |
67 | gd.loadTools() | 67 | gd.loadTools() | |
68 | gd.loadDeprecatedVars() | 68 | gd.loadDeprecatedVars() | |
69 | } | 69 | } | |
70 | 70 | |||
71 | func (gd *GlobalData) loadDistSites() { | 71 | func (gd *GlobalData) loadDistSites() { | |
72 | fname := gd.Pkgsrcdir + "/mk/fetch/sites.mk" | 72 | fname := gd.Pkgsrcdir + "/mk/fetch/sites.mk" | |
73 | lines := LoadExistingLines(fname, true) | 73 | lines := LoadExistingLines(fname, true) | |
74 | 74 | |||
75 | names := make(map[string]bool) | 75 | names := make(map[string]bool) | |
76 | url2name := make(map[string]string) | 76 | url2name := make(map[string]string) | |
77 | for _, line := range lines { | 77 | for _, line := range lines { | |
78 | if m, varname, _, urls, _ := MatchVarassign(line.Text); m { | 78 | if m, varname, _, _, urls, _ := MatchVarassign(line.Text); m { | |
79 | if hasPrefix(varname, "MASTER_SITE_") && varname != "MASTER_SITE_BACKUP" { | 79 | if hasPrefix(varname, "MASTER_SITE_") && varname != "MASTER_SITE_BACKUP" { | |
80 | names[varname] = true | 80 | names[varname] = true | |
81 | for _, url := range splitOnSpace(urls) { | 81 | for _, url := range splitOnSpace(urls) { | |
82 | if matches(url, `^(?:http://|https://|ftp://)`) { | 82 | if matches(url, `^(?:http://|https://|ftp://)`) { | |
83 | url2name[url] = varname | 83 | url2name[url] = varname | |
84 | } | 84 | } | |
85 | } | 85 | } | |
86 | } | 86 | } | |
87 | } | 87 | } | |
88 | } | 88 | } | |
89 | 89 | |||
90 | // Explicitly allowed, although not defined in mk/fetch/sites.mk. | 90 | // Explicitly allowed, although not defined in mk/fetch/sites.mk. | |
91 | names["MASTER_SITE_SUSE_UPD"] = true | 91 | names["MASTER_SITE_SUSE_UPD"] = true | |
@@ -129,27 +129,27 @@ func (gd *GlobalData) loadTools() { | @@ -129,27 +129,27 @@ func (gd *GlobalData) loadTools() { | |||
129 | Fatalf(toolFiles[0], noLines, "Too few tool files.") | 129 | Fatalf(toolFiles[0], noLines, "Too few tool files.") | |
130 | } | 130 | } | |
131 | 131 | |||
132 | tools := make(map[string]bool) | 132 | tools := make(map[string]bool) | |
133 | vartools := make(map[string]string) | 133 | vartools := make(map[string]string) | |
134 | predefinedTools := make(map[string]bool) | 134 | predefinedTools := make(map[string]bool) | |
135 | varnameToToolname := make(map[string]string) | 135 | varnameToToolname := make(map[string]string) | |
136 | systemBuildDefs := make(map[string]bool) | 136 | systemBuildDefs := make(map[string]bool) | |
137 | 137 | |||
138 | for _, basename := range toolFiles { | 138 | for _, basename := range toolFiles { | |
139 | fname := G.globalData.Pkgsrcdir + "/mk/tools/" + basename | 139 | fname := G.globalData.Pkgsrcdir + "/mk/tools/" + basename | |
140 | lines := LoadExistingLines(fname, true) | 140 | lines := LoadExistingLines(fname, true) | |
141 | for _, line := range lines { | 141 | for _, line := range lines { | |
142 | if m, varname, _, value, _ := MatchVarassign(line.Text); m { | 142 | if m, varname, _, _, value, _ := MatchVarassign(line.Text); m { | |
143 | if varname == "TOOLS_CREATE" && (value == "[" || matches(value, `^?[-\w.]+$`)) { | 143 | if varname == "TOOLS_CREATE" && (value == "[" || matches(value, `^?[-\w.]+$`)) { | |
144 | tools[value] = true | 144 | tools[value] = true | |
145 | } else if m, toolname := match1(varname, `^(?:_TOOLS_VARNAME)\.([-\w.]+|\[)$`); m { | 145 | } else if m, toolname := match1(varname, `^(?:_TOOLS_VARNAME)\.([-\w.]+|\[)$`); m { | |
146 | tools[toolname] = true | 146 | tools[toolname] = true | |
147 | vartools[toolname] = value | 147 | vartools[toolname] = value | |
148 | varnameToToolname[value] = toolname | 148 | varnameToToolname[value] = toolname | |
149 | 149 | |||
150 | } else if m, toolname := match1(varname, `^(?:TOOLS_PATH|_TOOLS_DEPMETHOD)\.([-\w.]+|\[)$`); m { | 150 | } else if m, toolname := match1(varname, `^(?:TOOLS_PATH|_TOOLS_DEPMETHOD)\.([-\w.]+|\[)$`); m { | |
151 | tools[toolname] = true | 151 | tools[toolname] = true | |
152 | 152 | |||
153 | } else if m, toolname := match1(varname, `_TOOLS\.(.*)`); m { | 153 | } else if m, toolname := match1(varname, `_TOOLS\.(.*)`); m { | |
154 | tools[toolname] = true | 154 | tools[toolname] = true | |
155 | for _, tool := range splitOnSpace(value) { | 155 | for _, tool := range splitOnSpace(value) { | |
@@ -159,27 +159,27 @@ func (gd *GlobalData) loadTools() { | @@ -159,27 +159,27 @@ func (gd *GlobalData) loadTools() { | |||
159 | } | 159 | } | |
160 | } | 160 | } | |
161 | } | 161 | } | |
162 | 162 | |||
163 | { | 163 | { | |
164 | basename := "bsd.pkg.mk" | 164 | basename := "bsd.pkg.mk" | |
165 | fname := G.globalData.Pkgsrcdir + "/mk/" + basename | 165 | fname := G.globalData.Pkgsrcdir + "/mk/" + basename | |
166 | condDepth := 0 | 166 | condDepth := 0 | |
167 | 167 | |||
168 | lines := LoadExistingLines(fname, true) | 168 | lines := LoadExistingLines(fname, true) | |
169 | for _, line := range lines { | 169 | for _, line := range lines { | |
170 | text := line.Text | 170 | text := line.Text | |
171 | 171 | |||
172 | if m, varname, _, value, _ := MatchVarassign(text); m { | 172 | if m, varname, _, _, value, _ := MatchVarassign(text); m { | |
173 | if varname == "USE_TOOLS" { | 173 | if varname == "USE_TOOLS" { | |
174 | if G.opts.DebugTools { | 174 | if G.opts.DebugTools { | |
175 | line.Debugf("[condDepth=%d] %s", condDepth, value) | 175 | line.Debugf("[condDepth=%d] %s", condDepth, value) | |
176 | } | 176 | } | |
177 | if condDepth == 0 { | 177 | if condDepth == 0 { | |
178 | for _, tool := range splitOnSpace(value) { | 178 | for _, tool := range splitOnSpace(value) { | |
179 | if !containsVarRef(tool) && tools[tool] { | 179 | if !containsVarRef(tool) && tools[tool] { | |
180 | predefinedTools[tool] = true | 180 | predefinedTools[tool] = true | |
181 | predefinedTools["TOOLS_"+tool] = true | 181 | predefinedTools["TOOLS_"+tool] = true | |
182 | } | 182 | } | |
183 | } | 183 | } | |
184 | } | 184 | } | |
185 | 185 |
@@ -22,69 +22,228 @@ func (s *Suite) TestChecklineMkVartype_S | @@ -22,69 +22,228 @@ func (s *Suite) TestChecklineMkVartype_S | |||
22 | 22 | |||
23 | mkline.CheckVartype("COMMENT", opAssign, "A nice package", "") | 23 | mkline.CheckVartype("COMMENT", opAssign, "A nice package", "") | |
24 | 24 | |||
25 | c.Check(s.Stdout(), equals, "WARN: fname:1: COMMENT should not begin with \"A\".\n") | 25 | c.Check(s.Stdout(), equals, "WARN: fname:1: COMMENT should not begin with \"A\".\n") | |
26 | } | 26 | } | |
27 | 27 | |||
28 | func (s *Suite) TestChecklineMkVartype(c *check.C) { | 28 | func (s *Suite) TestChecklineMkVartype(c *check.C) { | |
29 | G.globalData.InitVartypes() | 29 | G.globalData.InitVartypes() | |
30 | mkline := NewMkLine(NewLine("fname", 1, "DISTNAME=gcc-${GCC_VERSION}", nil)) | 30 | mkline := NewMkLine(NewLine("fname", 1, "DISTNAME=gcc-${GCC_VERSION}", nil)) | |
31 | 31 | |||
32 | mkline.CheckVartype("DISTNAME", opAssign, "gcc-${GCC_VERSION}", "") | 32 | mkline.CheckVartype("DISTNAME", opAssign, "gcc-${GCC_VERSION}", "") | |
33 | } | 33 | } | |
34 | 34 | |||
35 | func (s *Suite) TestChecklineMkVaralign(c *check.C) { | 35 | func (s *Suite) TestMkLine_CheckVaralign_Autofix(c *check.C) { | |
36 | s.UseCommandLine(c, "-Wspace", "-f") | 36 | s.UseCommandLine(c, "-Wspace", "-f") | |
37 | lines := s.NewLines("file.mk", | 37 | lines := s.NewLines("file.mk", | |
38 | "VAR= value", // Indentation 7, fixed to 8. | 38 | "VAR= value", // Indentation 7, fixed to 8. | |
39 | "", // | |||
39 | "VAR= value", // Indentation 8, fixed to 8. | 40 | "VAR= value", // Indentation 8, fixed to 8. | |
40 | "VAR= value", // Indentation 9, fixed to 16. | 41 | "", // | |
42 | "VAR= value", // Indentation 9, fixed to 8. | |||
43 | "", // | |||
41 | "VAR= \tvalue", // Mixed indentation 8, fixed to 8. | 44 | "VAR= \tvalue", // Mixed indentation 8, fixed to 8. | |
45 | "", // | |||
42 | "VAR= \tvalue", // Mixed indentation 8, fixed to 8. | 46 | "VAR= \tvalue", // Mixed indentation 8, fixed to 8. | |
43 | "VAR= \tvalue", // Mixed indentation 16, fixed to 16. | 47 | "", // | |
48 | "VAR= \tvalue", // Mixed indentation 16, fixed to 8. | |||
49 | "", // | |||
44 | "VAR=\tvalue") // Already aligned with tabs only, left unchanged. | 50 | "VAR=\tvalue") // Already aligned with tabs only, left unchanged. | |
45 | 51 | |||
52 | varalign := new(VaralignBlock) | |||
46 | for _, line := range lines { | 53 | for _, line := range lines { | |
47 | NewMkLine(line).CheckVaralign() | 54 | varalign.Check(NewMkLine(line)) | |
48 | } | 55 | } | |
56 | varalign.Finish() | |||
49 | 57 | |||
50 | c.Check(lines[0].changed, equals, true) | 58 | c.Check(lines[0].changed, equals, true) | |
51 | c.Check(lines[0].rawLines()[0].String(), equals, "1:VAR=\tvalue\n") | 59 | c.Check(lines[0].rawLines()[0].String(), equals, "1:VAR=\tvalue\n") | |
52 | c.Check(lines[1].changed, equals, true) | |||
53 | c.Check(lines[1].rawLines()[0].String(), equals, "2:VAR=\tvalue\n") | |||
54 | c.Check(lines[2].changed, equals, true) | 60 | c.Check(lines[2].changed, equals, true) | |
55 | c.Check(lines[2].rawLines()[0].String(), equals, "3:VAR=\t\tvalue\n") | 61 | c.Check(lines[2].rawLines()[0].String(), equals, "3:VAR=\tvalue\n") | |
56 | c.Check(lines[3].changed, equals, true) | |||
57 | c.Check(lines[3].rawLines()[0].String(), equals, "4:VAR=\tvalue\n") | |||
58 | c.Check(lines[4].changed, equals, true) | 62 | c.Check(lines[4].changed, equals, true) | |
59 | c.Check(lines[4].rawLines()[0].String(), equals, "5:VAR=\tvalue\n") | 63 | c.Check(lines[4].rawLines()[0].String(), equals, "5:VAR=\tvalue\n") | |
60 | c.Check(lines[5].changed, equals, true) | 64 | c.Check(lines[6].changed, equals, true) | |
61 | c.Check(lines[5].rawLines()[0].String(), equals, "6:VAR=\t\tvalue\n") | |||
62 | c.Check(lines[6].changed, equals, false) | |||
63 | c.Check(lines[6].rawLines()[0].String(), equals, "7:VAR=\tvalue\n") | 65 | c.Check(lines[6].rawLines()[0].String(), equals, "7:VAR=\tvalue\n") | |
66 | c.Check(lines[8].changed, equals, true) | |||
67 | c.Check(lines[8].rawLines()[0].String(), equals, "9:VAR=\tvalue\n") | |||
68 | c.Check(lines[10].changed, equals, true) | |||
69 | c.Check(lines[10].rawLines()[0].String(), equals, "11:VAR=\tvalue\n") | |||
70 | c.Check(lines[12].changed, equals, false) | |||
71 | c.Check(lines[12].rawLines()[0].String(), equals, "13:VAR=\tvalue\n") | |||
64 | c.Check(s.Output(), equals, ""+ | 72 | c.Check(s.Output(), equals, ""+ | |
65 | "NOTE: file.mk:1: Alignment of variable values should be done with tabs, not spaces.\n"+ | 73 | "NOTE: file.mk:1: This variable value should be aligned with tabs, not spaces, to column 9.\n"+ | |
66 | "AUTOFIX: file.mk:1: Replacing \"VAR= \" with \"VAR=\\t\".\n"+ | 74 | "AUTOFIX: file.mk:1: Replacing \"VAR= \" with \"VAR=\\t\".\n"+ | |
67 | "NOTE: file.mk:2: Alignment of variable values should be done with tabs, not spaces.\n"+ | 75 | "NOTE: file.mk:3: Variable values should be aligned with tabs, not spaces.\n"+ | |
68 | "AUTOFIX: file.mk:2: Replacing \"VAR= \" with \"VAR=\\t\".\n"+ | 76 | "AUTOFIX: file.mk:3: Replacing \"VAR= \" with \"VAR=\\t\".\n"+ | |
69 | "NOTE: file.mk:3: Alignment of variable values should be done with tabs, not spaces.\n"+ | 77 | "NOTE: file.mk:5: This variable value should be aligned with tabs, not spaces, to column 9.\n"+ | |
70 | "AUTOFIX: file.mk:3: Replacing \"VAR= \" with \"VAR=\\t\\t\".\n"+ | 78 | "AUTOFIX: file.mk:5: Replacing \"VAR= \" with \"VAR=\\t\".\n"+ | |
71 | "NOTE: file.mk:4: Alignment of variable values should be done with tabs, not spaces.\n"+ | 79 | "NOTE: file.mk:7: Variable values should be aligned with tabs, not spaces.\n"+ | |
72 | "AUTOFIX: file.mk:4: Replacing \"VAR= \\t\" with \"VAR=\\t\".\n"+ | 80 | "AUTOFIX: file.mk:7: Replacing \"VAR= \\t\" with \"VAR=\\t\".\n"+ | |
73 | "NOTE: file.mk:5: Alignment of variable values should be done with tabs, not spaces.\n"+ | 81 | "NOTE: file.mk:9: Variable values should be aligned with tabs, not spaces.\n"+ | |
74 | "AUTOFIX: file.mk:5: Replacing \"VAR= \\t\" with \"VAR=\\t\".\n"+ | 82 | "AUTOFIX: file.mk:9: Replacing \"VAR= \\t\" with \"VAR=\\t\".\n"+ | |
75 | "NOTE: file.mk:6: Alignment of variable values should be done with tabs, not spaces.\n"+ | 83 | "NOTE: file.mk:11: This variable value should be aligned with tabs, not spaces, to column 9.\n"+ | |
76 | "AUTOFIX: file.mk:6: Replacing \"VAR= \\t\" with \"VAR=\\t\\t\".\n") | 84 | "AUTOFIX: file.mk:11: Replacing \"VAR= \\t\" with \"VAR=\\t\".\n") | |
77 | c.Check(tabLength("VAR= \t"), equals, 16) | 85 | } | |
86 | ||||
87 | func (s *Suite) TestMkLine_CheckVaralign_ReduceIndentation(c *check.C) { | |||
88 | s.UseCommandLine(c, "-Wspace") | |||
89 | mklines := s.NewMkLines("file.mk", | |||
90 | "VAR= \tvalue", | |||
91 | "VAR= \tvalue", | |||
92 | "VAR=\t\t\t\tvalue", | |||
93 | "", | |||
94 | "VAR=\t\t\tneedlessly", // Nothing to be fixed here, since it looks good. | |||
95 | "VAR=\t\t\tdeep", | |||
96 | "VAR=\t\t\tindentation") | |||
97 | ||||
98 | varalign := new(VaralignBlock) | |||
99 | for _, mkline := range mklines.mklines { | |||
100 | varalign.Check(mkline) | |||
101 | } | |||
102 | varalign.Finish() | |||
103 | ||||
104 | c.Check(s.Output(), equals, ""+ | |||
105 | "NOTE: file.mk:1: Variable values should be aligned with tabs, not spaces.\n"+ | |||
106 | "NOTE: file.mk:2: This variable value should be aligned with tabs, not spaces, to column 9.\n"+ | |||
107 | "NOTE: file.mk:3: This variable value should be aligned to column 9.\n") | |||
108 | } | |||
109 | ||||
110 | func (s *Suite) TestMkLine_CheckVaralign_LongestLineEmptyAlignment(c *check.C) { | |||
111 | s.UseCommandLine(c, "-Wspace") | |||
112 | mklines := s.NewMkLines("file.mk", | |||
113 | "SUBST_CLASSES+= aaaaaaaa", | |||
114 | "SUBST_STAGE.aaaaaaaa= pre-configure", | |||
115 | "SUBST_FILES.aaaaaaaa= *.pl", | |||
116 | "SUBST_FILTER_CMD.aaaaaaaa=cat") | |||
117 | ||||
118 | varalign := new(VaralignBlock) | |||
119 | for _, mkline := range mklines.mklines { | |||
120 | varalign.Check(mkline) | |||
121 | } | |||
122 | varalign.Finish() | |||
123 | ||||
124 | c.Check(s.Output(), equals, ""+ | |||
125 | "NOTE: file.mk:1: This variable value should be aligned with tabs, not spaces, to column 33.\n"+ | |||
126 | "NOTE: file.mk:2: This variable value should be aligned with tabs, not spaces, to column 33.\n"+ | |||
127 | "NOTE: file.mk:3: This variable value should be aligned with tabs, not spaces, to column 33.\n"+ | |||
128 | "NOTE: file.mk:4: This variable value should be aligned to column 33.\n") | |||
129 | } | |||
130 | ||||
131 | func (s *Suite) TestMkLine_CheckVaralign_OnlySpaces(c *check.C) { | |||
132 | s.UseCommandLine(c, "-Wspace") | |||
133 | mklines := s.NewMkLines("file.mk", | |||
134 | "SUBST_CLASSES+= aaaaaaaa", | |||
135 | "SUBST_STAGE.aaaaaaaa= pre-configure", | |||
136 | "SUBST_FILES.aaaaaaaa= *.pl", | |||
137 | "SUBST_FILTER_CMD.aaaaaaaa= cat") | |||
138 | ||||
139 | varalign := new(VaralignBlock) | |||
140 | for _, mkline := range mklines.mklines { | |||
141 | varalign.Check(mkline) | |||
142 | } | |||
143 | varalign.Finish() | |||
144 | ||||
145 | c.Check(s.Output(), equals, ""+ | |||
146 | "NOTE: file.mk:1: This variable value should be aligned with tabs, not spaces, to column 33.\n"+ | |||
147 | "NOTE: file.mk:2: This variable value should be aligned with tabs, not spaces, to column 33.\n"+ | |||
148 | "NOTE: file.mk:3: This variable value should be aligned with tabs, not spaces, to column 33.\n"+ | |||
149 | "NOTE: file.mk:4: This variable value should be aligned with tabs, not spaces, to column 33.\n") | |||
150 | } | |||
151 | ||||
152 | func (s *Suite) TestMkLine_CheckVaralign_Advanced(c *check.C) { | |||
153 | s.UseCommandLine(c, "-Wspace") | |||
154 | fname := s.CreateTmpFileLines(c, "Makefile", | |||
155 | "# $"+"NetBSD$", | |||
156 | "", | |||
157 | "VAR= \\", // In continuation lines, indenting with spaces is ok | |||
158 | "\tvalue", | |||
159 | "", | |||
160 | "VAR= indented with one space", // Exactly one space is ok in general | |||
161 | "VAR= indented with two spaces", // Two spaces are uncommon | |||
162 | "", | |||
163 | "BLOCK=\tindented with tab", | |||
164 | "BLOCK_LONGVAR= indented with space", // This is ok, to prevent the block from being indented further | |||
165 | "", | |||
166 | "BLOCK=\tshort", | |||
167 | "BLOCK_LONGVAR=\tlong", | |||
168 | "", | |||
169 | "GRP_A= avalue", // The values in a block should be aligned | |||
170 | "GRP_AA= value", | |||
171 | "GRP_AAA= value", | |||
172 | "GRP_AAAA= value", | |||
173 | "", | |||
174 | "VAR=\t${VAR}${BLOCK}${BLOCK_LONGVAR} # suppress warnings about unused variables", | |||
175 | "VAR=\t${GRP_A}${GRP_AA}${GRP_AAA}${GRP_AAAA}") | |||
176 | mklines := NewMkLines(LoadExistingLines(fname, true)) | |||
177 | ||||
178 | mklines.Check() | |||
179 | ||||
180 | c.Check(s.OutputCleanTmpdir(), equals, ""+ | |||
181 | "NOTE: ~/Makefile:6: This variable value should be aligned with tabs, not spaces, to column 9.\n"+ | |||
182 | "NOTE: ~/Makefile:7: This variable value should be aligned with tabs, not spaces, to column 9.\n"+ | |||
183 | "NOTE: ~/Makefile:12: This variable value should be aligned to column 17.\n"+ | |||
184 | "NOTE: ~/Makefile:15: This variable value should be aligned with tabs, not spaces, to column 17.\n"+ | |||
185 | "NOTE: ~/Makefile:16: This variable value should be aligned with tabs, not spaces, to column 17.\n"+ | |||
186 | "NOTE: ~/Makefile:17: This variable value should be aligned with tabs, not spaces, to column 17.\n"+ | |||
187 | "NOTE: ~/Makefile:18: This variable value should be aligned with tabs, not spaces, to column 17.\n") | |||
188 | ||||
189 | s.UseCommandLine(c, "-Wspace", "--autofix") | |||
190 | ||||
191 | mklines.Check() | |||
192 | ||||
193 | c.Check(s.OutputCleanTmpdir(), equals, ""+ | |||
194 | "AUTOFIX: ~/Makefile:6: Replacing \"VAR= \" with \"VAR=\\t\".\n"+ | |||
195 | "AUTOFIX: ~/Makefile:7: Replacing \"VAR= \" with \"VAR=\\t\".\n"+ | |||
196 | "AUTOFIX: ~/Makefile:12: Replacing \"BLOCK=\\t\" with \"BLOCK=\\t\\t\".\n"+ | |||
197 | "AUTOFIX: ~/Makefile:15: Replacing \"GRP_A= \" with \"GRP_A=\\t\\t\".\n"+ | |||
198 | "AUTOFIX: ~/Makefile:16: Replacing \"GRP_AA= \" with \"GRP_AA=\\t\\t\".\n"+ | |||
199 | "AUTOFIX: ~/Makefile:17: Replacing \"GRP_AAA= \" with \"GRP_AAA=\\t\".\n"+ | |||
200 | "AUTOFIX: ~/Makefile:18: Replacing \"GRP_AAAA= \" with \"GRP_AAAA=\\t\".\n"+ | |||
201 | "AUTOFIX: ~/Makefile: Has been auto-fixed. Please re-run pkglint.\n") | |||
202 | c.Check(s.LoadTmpFile(c, "Makefile"), equals, ""+ | |||
203 | "# $NetBSD: mkline_test.go,v 1.7 2016/01/24 02:03:28 rillig Exp $\n"+ | |||
204 | "\n"+ | |||
205 | "VAR= \\\n"+ | |||
206 | "\tvalue\n"+ | |||
207 | "\n"+ | |||
208 | "VAR=\tindented with one space\n"+ | |||
209 | "VAR=\tindented with two spaces\n"+ | |||
210 | "\n"+ | |||
211 | "BLOCK=\tindented with tab\n"+ | |||
212 | "BLOCK_LONGVAR= indented with space\n"+ | |||
213 | "\n"+ | |||
214 | "BLOCK=\t\tshort\n"+ | |||
215 | "BLOCK_LONGVAR=\tlong\n"+ | |||
216 | "\n"+ | |||
217 | "GRP_A=\t\tavalue\n"+ | |||
218 | "GRP_AA=\t\tvalue\n"+ | |||
219 | "GRP_AAA=\tvalue\n"+ | |||
220 | "GRP_AAAA=\tvalue\n"+ | |||
221 | "\n"+ | |||
222 | "VAR=\t${VAR}${BLOCK}${BLOCK_LONGVAR} # suppress warnings about unused variables\n"+ | |||
223 | "VAR=\t${GRP_A}${GRP_AA}${GRP_AAA}${GRP_AAAA}\n") | |||
224 | } | |||
225 | ||||
226 | func (s *Suite) TestMkLine_CheckVaralign_Misc(c *check.C) { | |||
227 | s.UseCommandLine(c, "-Wspace") | |||
228 | mklines := s.NewMkLines("Makefile", | |||
229 | "# $"+"NetBSD$", | |||
230 | "", | |||
231 | "VAR= space", | |||
232 | "VAR=\ttab ${VAR}") | |||
233 | ||||
234 | mklines.Check() | |||
235 | ||||
236 | c.Check(s.Output(), equals, "NOTE: Makefile:3: Variable values should be aligned with tabs, not spaces.\n") | |||
78 | } | 237 | } | |
79 | 238 | |||
80 | func (s *Suite) TestMkLine_fields(c *check.C) { | 239 | func (s *Suite) TestMkLine_fields(c *check.C) { | |
81 | mklines := NewMkLines(s.NewLines("test.mk", | 240 | mklines := NewMkLines(s.NewLines("test.mk", | |
82 | "VARNAME.param?=value # varassign comment", | 241 | "VARNAME.param?=value # varassign comment", | |
83 | "\tshell command # shell comment", | 242 | "\tshell command # shell comment", | |
84 | "# whole line comment", | 243 | "# whole line comment", | |
85 | "", | 244 | "", | |
86 | ". if !empty(PKGNAME:M*-*) # cond comment", | 245 | ". if !empty(PKGNAME:M*-*) # cond comment", | |
87 | ".include \"../../mk/bsd.prefs.mk\" # include comment", | 246 | ".include \"../../mk/bsd.prefs.mk\" # include comment", | |
88 | ".include <subdir.mk> # sysinclude comment", | 247 | ".include <subdir.mk> # sysinclude comment", | |
89 | "target1 target2: source1 source2", | 248 | "target1 target2: source1 source2", | |
90 | "target : source", | 249 | "target : source", | |
@@ -275,27 +434,28 @@ func (s *Suite) TestMkLine_CheckVarusePe | @@ -275,27 +434,28 @@ func (s *Suite) TestMkLine_CheckVarusePe | |||
275 | "COMMENT:=\t${PKGBASE}", | 434 | "COMMENT:=\t${PKGBASE}", | |
276 | "PYPKGPREFIX=${PKGBASE}") | 435 | "PYPKGPREFIX=${PKGBASE}") | |
277 | G.globalData.UserDefinedVars = map[string]*MkLine{ | 436 | G.globalData.UserDefinedVars = map[string]*MkLine{ | |
278 | "GAMES_USER": mklines.mklines[0], | 437 | "GAMES_USER": mklines.mklines[0], | |
279 | } | 438 | } | |
280 | 439 | |||
281 | mklines.Check() | 440 | mklines.Check() | |
282 | 441 | |||
283 | c.Check(s.Output(), equals, ""+ | 442 | c.Check(s.Output(), equals, ""+ | |
284 | "WARN: options.mk:2: The user-defined variable GAMES_USER is used but not added to BUILD_DEFS.\n"+ | 443 | "WARN: options.mk:2: The user-defined variable GAMES_USER is used but not added to BUILD_DEFS.\n"+ | |
285 | "WARN: options.mk:3: PKGBASE should not be evaluated at load time.\n"+ | 444 | "WARN: options.mk:3: PKGBASE should not be evaluated at load time.\n"+ | |
286 | "WARN: options.mk:4: The variable PYPKGPREFIX may not be set in this file; it would be ok in pyversion.mk.\n"+ | 445 | "WARN: options.mk:4: The variable PYPKGPREFIX may not be set in this file; it would be ok in pyversion.mk.\n"+ | |
287 | "WARN: options.mk:4: \"${PKGBASE}\" is not valid for PYPKGPREFIX. Use one of { py27 py33 py34 } instead.\n"+ | 446 | "WARN: options.mk:4: \"${PKGBASE}\" is not valid for PYPKGPREFIX. Use one of { py27 py33 py34 } instead.\n"+ | |
288 | "WARN: options.mk:4: PKGBASE should not be evaluated indirectly at load time.\n") | 447 | "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") | |||
289 | } | 449 | } | |
290 | 450 | |||
291 | func (s *Suite) TestMkLine_WarnVaruseLocalbase(c *check.C) { | 451 | func (s *Suite) TestMkLine_WarnVaruseLocalbase(c *check.C) { | |
292 | mkline := NewMkLine(NewLine("options.mk", 56, "PKGNAME=${LOCALBASE}", nil)) | 452 | mkline := NewMkLine(NewLine("options.mk", 56, "PKGNAME=${LOCALBASE}", nil)) | |
293 | 453 | |||
294 | mkline.WarnVaruseLocalbase() | 454 | mkline.WarnVaruseLocalbase() | |
295 | 455 | |||
296 | c.Check(s.Output(), equals, "WARN: options.mk:56: The LOCALBASE variable should not be used by packages.\n") | 456 | c.Check(s.Output(), equals, "WARN: options.mk:56: The LOCALBASE variable should not be used by packages.\n") | |
297 | } | 457 | } | |
298 | 458 | |||
299 | func (s *Suite) TestMkLine_Misc(c *check.C) { | 459 | func (s *Suite) TestMkLine_Misc(c *check.C) { | |
300 | s.UseCommandLine(c, "-Wextra") | 460 | s.UseCommandLine(c, "-Wextra") | |
301 | G.globalData.InitVartypes() | 461 | G.globalData.InitVartypes() |
@@ -293,27 +293,27 @@ func Checkfile(fname string) { | @@ -293,27 +293,27 @@ func Checkfile(fname string) { | |||
293 | } | 293 | } | |
294 | 294 | |||
295 | func ChecklinesTrailingEmptyLines(lines []*Line) { | 295 | func ChecklinesTrailingEmptyLines(lines []*Line) { | |
296 | max := len(lines) | 296 | max := len(lines) | |
297 | last := max | 297 | last := max | |
298 | for last > 1 && lines[last-1].Text == "" { | 298 | for last > 1 && lines[last-1].Text == "" { | |
299 | last-- | 299 | last-- | |
300 | } | 300 | } | |
301 | if last != max { | 301 | if last != max { | |
302 | lines[last].Note0("Trailing empty lines.") | 302 | lines[last].Note0("Trailing empty lines.") | |
303 | } | 303 | } | |
304 | } | 304 | } | |
305 | 305 | |||
306 | func MatchVarassign(text string) (m bool, varname, op, value, comment string) { | 306 | func MatchVarassign(text string) (m bool, varname, op, valueAlign, value, comment string) { | |
307 | i, n := 0, len(text) | 307 | i, n := 0, len(text) | |
308 | 308 | |||
309 | for i < n && text[i] == ' ' { | 309 | for i < n && text[i] == ' ' { | |
310 | i++ | 310 | i++ | |
311 | } | 311 | } | |
312 | 312 | |||
313 | varnameStart := i | 313 | varnameStart := i | |
314 | for ; i < n; i++ { | 314 | for ; i < n; i++ { | |
315 | b := text[i] | 315 | b := text[i] | |
316 | mask := uint(0) | 316 | mask := uint(0) | |
317 | switch b & 0xE0 { | 317 | switch b & 0xE0 { | |
318 | case 0x20: | 318 | case 0x20: | |
319 | mask = 0x03ff6c10 | 319 | mask = 0x03ff6c10 | |
@@ -328,27 +328,27 @@ func MatchVarassign(text string) (m bool | @@ -328,27 +328,27 @@ func MatchVarassign(text string) (m bool | |||
328 | } | 328 | } | |
329 | varnameEnd := i | 329 | varnameEnd := i | |
330 | 330 | |||
331 | if varnameEnd == varnameStart { | 331 | if varnameEnd == varnameStart { | |
332 | return | 332 | return | |
333 | } | 333 | } | |
334 | 334 | |||
335 | for i < n && (text[i] == ' ' || text[i] == '\t') { | 335 | for i < n && (text[i] == ' ' || text[i] == '\t') { | |
336 | i++ | 336 | i++ | |
337 | } | 337 | } | |
338 | 338 | |||
339 | opStart := i | 339 | opStart := i | |
340 | if i < n { | 340 | if i < n { | |
341 | if b := text[i]; b&0xE0 == 0x20 && (uint(0x84000802)>>(b&0x1F))&1 != 0 { | 341 | if b := text[i]; b == '!' || b == '+' || b == ':' || b == '?' { | |
342 | i++ | 342 | i++ | |
343 | } | 343 | } | |
344 | } | 344 | } | |
345 | if i < n && text[i] == '=' { | 345 | if i < n && text[i] == '=' { | |
346 | i++ | 346 | i++ | |
347 | } else { | 347 | } else { | |
348 | return | 348 | return | |
349 | } | 349 | } | |
350 | opEnd := i | 350 | opEnd := i | |
351 | 351 | |||
352 | if text[varnameEnd-1] == '+' && varnameEnd == opStart && text[opStart] == '=' { | 352 | if text[varnameEnd-1] == '+' && varnameEnd == opStart && text[opStart] == '=' { | |
353 | varnameEnd-- | 353 | varnameEnd-- | |
354 | opStart-- | 354 | opStart-- | |
@@ -367,26 +367,27 @@ func MatchVarassign(text string) (m bool | @@ -367,26 +367,27 @@ func MatchVarassign(text string) (m bool | |||
367 | break | 367 | break | |
368 | } else if b != '\\' || i+1 >= n || text[i+1] != '#' { | 368 | } else if b != '\\' || i+1 >= n || text[i+1] != '#' { | |
369 | valuebuf[j] = b | 369 | valuebuf[j] = b | |
370 | j++ | 370 | j++ | |
371 | } | 371 | } | |
372 | } | 372 | } | |
373 | 373 | |||
374 | commentStart := i | 374 | commentStart := i | |
375 | commentEnd := n | 375 | commentEnd := n | |
376 | 376 | |||
377 | m = true | 377 | m = true | |
378 | varname = text[varnameStart:varnameEnd] | 378 | varname = text[varnameStart:varnameEnd] | |
379 | op = text[opStart:opEnd] | 379 | op = text[opStart:opEnd] | |
380 | valueAlign = text[0:valueStart] | |||
380 | value = strings.TrimSpace(string(valuebuf[:j])) | 381 | value = strings.TrimSpace(string(valuebuf[:j])) | |
381 | comment = text[commentStart:commentEnd] | 382 | comment = text[commentStart:commentEnd] | |
382 | return | 383 | return | |
383 | } | 384 | } | |
384 | 385 | |||
385 | type DependencyPattern struct { | 386 | type DependencyPattern struct { | |
386 | pkgbase string // "freeciv-client", "{gcc48,gcc48-libs}", "${EMACS_REQD}" | 387 | pkgbase string // "freeciv-client", "{gcc48,gcc48-libs}", "${EMACS_REQD}" | |
387 | lowerOp string // ">=", ">" | 388 | lowerOp string // ">=", ">" | |
388 | lower string // "2.5.0", "${PYVER}" | 389 | lower string // "2.5.0", "${PYVER}" | |
389 | upperOp string // "<", "<=" | 390 | upperOp string // "<", "<=" | |
390 | upper string // "3.0", "${PYVER}" | 391 | upper string // "3.0", "${PYVER}" | |
391 | wildcard string // "[0-9]*", "1.5.*", "${PYVER}" | 392 | wildcard string // "[0-9]*", "1.5.*", "${PYVER}" | |
392 | } | 393 | } |
@@ -5,26 +5,27 @@ package main | @@ -5,26 +5,27 @@ package main | |||
5 | import ( | 5 | import ( | |
6 | "fmt" | 6 | "fmt" | |
7 | "os" | 7 | "os" | |
8 | "strconv" | 8 | "strconv" | |
9 | "strings" | 9 | "strings" | |
10 | ) | 10 | ) | |
11 | 11 | |||
12 | type MkLine struct { | 12 | type MkLine struct { | |
13 | Line *Line | 13 | Line *Line | |
14 | 14 | |||
15 | xtype uint8 | 15 | xtype uint8 | |
16 | xmustexist bool | 16 | xmustexist bool | |
17 | xop MkOperator | 17 | xop MkOperator | |
18 | xvalign string | |||
18 | xs1 string | 19 | xs1 string | |
19 | xs2 string | 20 | xs2 string | |
20 | xs3 string | 21 | xs3 string | |
21 | xvalue string | 22 | xvalue string | |
22 | xcomment string | 23 | xcomment string | |
23 | } | 24 | } | |
24 | 25 | |||
25 | func (mkline *MkLine) Error1(format, arg1 string) { mkline.Line.Error1(format, arg1) } | 26 | func (mkline *MkLine) Error1(format, arg1 string) { mkline.Line.Error1(format, arg1) } | |
26 | func (mkline *MkLine) Warn0(format string) { mkline.Line.Warn0(format) } | 27 | func (mkline *MkLine) Warn0(format string) { mkline.Line.Warn0(format) } | |
27 | func (mkline *MkLine) Warn1(format, arg1 string) { mkline.Line.Warn1(format, arg1) } | 28 | func (mkline *MkLine) Warn1(format, arg1 string) { mkline.Line.Warn1(format, arg1) } | |
28 | func (mkline *MkLine) Warn2(format, arg1, arg2 string) { mkline.Line.Warn2(format, arg1, arg2) } | 29 | func (mkline *MkLine) Warn2(format, arg1, arg2 string) { mkline.Line.Warn2(format, arg1, arg2) } | |
29 | func (mkline *MkLine) Note0(format string) { mkline.Line.Note0(format) } | 30 | func (mkline *MkLine) Note0(format string) { mkline.Line.Note0(format) } | |
30 | func (mkline *MkLine) Note2(format, arg1, arg2 string) { mkline.Line.Note2(format, arg1, arg2) } | 31 | func (mkline *MkLine) Note2(format, arg1, arg2 string) { mkline.Line.Note2(format, arg1, arg2) } | |
@@ -34,35 +35,36 @@ func (mkline *MkLine) Debug2(format, arg | @@ -34,35 +35,36 @@ func (mkline *MkLine) Debug2(format, arg | |||
34 | func NewMkLine(line *Line) (mkline *MkLine) { | 35 | func NewMkLine(line *Line) (mkline *MkLine) { | |
35 | mkline = &MkLine{Line: line} | 36 | mkline = &MkLine{Line: line} | |
36 | 37 | |||
37 | text := line.Text | 38 | text := line.Text | |
38 | 39 | |||
39 | if hasPrefix(text, " ") { | 40 | if hasPrefix(text, " ") { | |
40 | mkline.Warn0("Makefile lines should not start with space characters.") | 41 | mkline.Warn0("Makefile lines should not start with space characters.") | |
41 | Explain3( | 42 | Explain3( | |
42 | "If you want this line to contain a shell program, use a tab", | 43 | "If you want this line to contain a shell program, use a tab", | |
43 | "character for indentation. Otherwise please remove the leading", | 44 | "character for indentation. Otherwise please remove the leading", | |
44 | "white-space.") | 45 | "white-space.") | |
45 | } | 46 | } | |
46 | 47 | |||
47 | if m, varname, op, value, comment := MatchVarassign(text); m { | 48 | if m, varname, op, valueAlign, value, comment := MatchVarassign(text); m { | |
48 | value = strings.Replace(value, "\\#", "#", -1) | 49 | value = strings.Replace(value, "\\#", "#", -1) | |
49 | varparam := varnameParam(varname) | 50 | varparam := varnameParam(varname) | |
50 | 51 | |||
51 | mkline.xtype = 1 | 52 | mkline.xtype = 1 | |
52 | mkline.xs1 = varname | 53 | mkline.xs1 = varname | |
53 | mkline.xs2 = varnameCanon(varname) | 54 | mkline.xs2 = varnameCanon(varname) | |
54 | mkline.xs3 = varparam | 55 | mkline.xs3 = varparam | |
55 | mkline.xop = NewMkOperator(op) | 56 | mkline.xop = NewMkOperator(op) | |
57 | mkline.xvalign = valueAlign | |||
56 | mkline.xvalue = value | 58 | mkline.xvalue = value | |
57 | mkline.xcomment = comment | 59 | mkline.xcomment = comment | |
58 | mkline.Tokenize(value) | 60 | mkline.Tokenize(value) | |
59 | return | 61 | return | |
60 | } | 62 | } | |
61 | 63 | |||
62 | if hasPrefix(text, "\t") { | 64 | if hasPrefix(text, "\t") { | |
63 | mkline.xtype = 2 | 65 | mkline.xtype = 2 | |
64 | mkline.xs1 = text[1:] | 66 | mkline.xs1 = text[1:] | |
65 | mkline.Tokenize(mkline.xs1) | 67 | mkline.Tokenize(mkline.xs1) | |
66 | return | 68 | return | |
67 | } | 69 | } | |
68 | 70 | |||
@@ -112,26 +114,27 @@ func NewMkLine(line *Line) (mkline *MkLi | @@ -112,26 +114,27 @@ func NewMkLine(line *Line) (mkline *MkLi | |||
112 | if matches(text, `^(<<<<<<<|=======|>>>>>>>)`) { | 114 | if matches(text, `^(<<<<<<<|=======|>>>>>>>)`) { | |
113 | return | 115 | return | |
114 | } | 116 | } | |
115 | 117 | |||
116 | line.Error0("Unknown Makefile line format.") | 118 | line.Error0("Unknown Makefile line format.") | |
117 | return mkline | 119 | return mkline | |
118 | } | 120 | } | |
119 | 121 | |||
120 | func (mkline *MkLine) IsVarassign() bool { return mkline.xtype == 1 } | 122 | func (mkline *MkLine) IsVarassign() bool { return mkline.xtype == 1 } | |
121 | func (mkline *MkLine) Varname() string { return mkline.xs1 } | 123 | func (mkline *MkLine) Varname() string { return mkline.xs1 } | |
122 | func (mkline *MkLine) Varcanon() string { return mkline.xs2 } | 124 | func (mkline *MkLine) Varcanon() string { return mkline.xs2 } | |
123 | func (mkline *MkLine) Varparam() string { return mkline.xs3 } | 125 | func (mkline *MkLine) Varparam() string { return mkline.xs3 } | |
124 | func (mkline *MkLine) Op() MkOperator { return mkline.xop } | 126 | func (mkline *MkLine) Op() MkOperator { return mkline.xop } | |
127 | func (mkline *MkLine) ValueAlign() string { return mkline.xvalign } | |||
125 | func (mkline *MkLine) Value() string { return mkline.xvalue } | 128 | func (mkline *MkLine) Value() string { return mkline.xvalue } | |
126 | func (mkline *MkLine) Comment() string { return mkline.xcomment } | 129 | func (mkline *MkLine) Comment() string { return mkline.xcomment } | |
127 | func (mkline *MkLine) IsShellcmd() bool { return mkline.xtype == 2 } | 130 | func (mkline *MkLine) IsShellcmd() bool { return mkline.xtype == 2 } | |
128 | func (mkline *MkLine) Shellcmd() string { return mkline.xs1 } | 131 | func (mkline *MkLine) Shellcmd() string { return mkline.xs1 } | |
129 | func (mkline *MkLine) IsComment() bool { return mkline.xtype == 3 } | 132 | func (mkline *MkLine) IsComment() bool { return mkline.xtype == 3 } | |
130 | func (mkline *MkLine) IsEmpty() bool { return mkline.xtype == 4 } | 133 | func (mkline *MkLine) IsEmpty() bool { return mkline.xtype == 4 } | |
131 | func (mkline *MkLine) IsCond() bool { return mkline.xtype == 5 } | 134 | func (mkline *MkLine) IsCond() bool { return mkline.xtype == 5 } | |
132 | func (mkline *MkLine) Indent() string { return mkline.xs1 } | 135 | func (mkline *MkLine) Indent() string { return mkline.xs1 } | |
133 | func (mkline *MkLine) Directive() string { return mkline.xs2 } | 136 | func (mkline *MkLine) Directive() string { return mkline.xs2 } | |
134 | func (mkline *MkLine) Args() string { return mkline.xs3 } | 137 | func (mkline *MkLine) Args() string { return mkline.xs3 } | |
135 | func (mkline *MkLine) IsInclude() bool { return mkline.xtype == 6 } | 138 | func (mkline *MkLine) IsInclude() bool { return mkline.xtype == 6 } | |
136 | func (mkline *MkLine) MustExist() bool { return mkline.xmustexist } | 139 | func (mkline *MkLine) MustExist() bool { return mkline.xmustexist } | |
137 | func (mkline *MkLine) Includefile() string { return mkline.xs1 } | 140 | func (mkline *MkLine) Includefile() string { return mkline.xs1 } | |
@@ -743,45 +746,26 @@ func (mkline *MkLine) withoutMakeVariabl | @@ -743,45 +746,26 @@ func (mkline *MkLine) withoutMakeVariabl | |||
743 | for { | 746 | for { | |
744 | var m []string | 747 | var m []string | |
745 | if m, valueNovar = replaceFirst(valueNovar, `\$\{([^{}]*)\}`, ""); m != nil { | 748 | if m, valueNovar = replaceFirst(valueNovar, `\$\{([^{}]*)\}`, ""); m != nil { | |
746 | varuse := m[1] | 749 | varuse := m[1] | |
747 | if !qModifierAllowed && hasSuffix(varuse, ":Q") { | 750 | if !qModifierAllowed && hasSuffix(varuse, ":Q") { | |
748 | mkline.Warn0("The :Q operator should only be used in lists and shell commands.") | 751 | mkline.Warn0("The :Q operator should only be used in lists and shell commands.") | |
749 | } | 752 | } | |
750 | } else { | 753 | } else { | |
751 | return valueNovar | 754 | return valueNovar | |
752 | } | 755 | } | |
753 | } | 756 | } | |
754 | } | 757 | } | |
755 | 758 | |||
756 | func (mkline *MkLine) CheckVaralign() { | |||
757 | if !G.opts.WarnSpace { | |||
758 | return | |||
759 | } | |||
760 | ||||
761 | if m, prefix, align := match2(mkline.Line.Text, `^( *[-*+A-Z_a-z0-9.${}\[]+\s*[!:?]?=)(\s*)`); m { | |||
762 | if align != " " && strings.Trim(align, "\t") != "" { | |||
763 | alignedWidth := tabLength(prefix + align) | |||
764 | tabs := "" | |||
765 | for tabLength(prefix+tabs) < alignedWidth { | |||
766 | tabs += "\t" | |||
767 | } | |||
768 | if !mkline.Line.AutofixReplace(prefix+align, prefix+tabs) { | |||
769 | mkline.Note0("Alignment of variable values should be done with tabs, not spaces.") | |||
770 | } | |||
771 | } | |||
772 | } | |||
773 | } | |||
774 | ||||
775 | func (mkline *MkLine) CheckText(text string) { | 759 | func (mkline *MkLine) CheckText(text string) { | |
776 | if G.opts.DebugTrace { | 760 | if G.opts.DebugTrace { | |
777 | defer tracecall1(text)() | 761 | defer tracecall1(text)() | |
778 | } | 762 | } | |
779 | 763 | |||
780 | if m, varname := match1(text, `^(?:[^#]*[^\$])?\$(\w+)`); m { | 764 | if m, varname := match1(text, `^(?:[^#]*[^\$])?\$(\w+)`); m { | |
781 | mkline.Warn1("$%[1]s is ambiguous. Use ${%[1]s} if you mean a Makefile variable or $$%[1]s if you mean a shell variable.", varname) | 765 | mkline.Warn1("$%[1]s is ambiguous. Use ${%[1]s} if you mean a Makefile variable or $$%[1]s if you mean a shell variable.", varname) | |
782 | } | 766 | } | |
783 | 767 | |||
784 | if mkline.Line.firstLine == 1 { | 768 | if mkline.Line.firstLine == 1 { | |
785 | mkline.Line.CheckRcsid(`# `, "# ") | 769 | mkline.Line.CheckRcsid(`# `, "# ") | |
786 | } | 770 | } | |
787 | 771 |
@@ -74,82 +74,82 @@ func (mklines *MkLines) UseVar(mkline *M | @@ -74,82 +74,82 @@ func (mklines *MkLines) UseVar(mkline *M | |||
74 | 74 | |||
75 | func (mklines *MkLines) VarValue(varname string) (value string, found bool) { | 75 | func (mklines *MkLines) VarValue(varname string) (value string, found bool) { | |
76 | if mkline := mklines.vardef[varname]; mkline != nil { | 76 | if mkline := mklines.vardef[varname]; mkline != nil { | |
77 | return mkline.Value(), true | 77 | return mkline.Value(), true | |
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 | allowedTargets := make(map[string]bool) | |||
88 | substcontext := new(SubstContext) | |||
89 | ||||
90 | G.Mk = mklines | 87 | G.Mk = mklines | |
91 | defer func() { G.Mk = nil }() | 88 | defer func() { G.Mk = nil }() | |
92 | 89 | |||
93 | mklines.DetermineUsedVariables() | 90 | allowedTargets := make(map[string]bool) | |
94 | ||||
95 | prefixes := splitOnSpace("pre do post") | 91 | prefixes := splitOnSpace("pre do post") | |
96 | actions := splitOnSpace("fetch extract patch tools wrapper configure build test install package clean") | 92 | actions := splitOnSpace("fetch extract patch tools wrapper configure build test install package clean") | |
97 | for _, prefix := range prefixes { | 93 | for _, prefix := range prefixes { | |
98 | for _, action := range actions { | 94 | for _, action := range actions { | |
99 | allowedTargets[prefix+"-"+action] = true | 95 | allowedTargets[prefix+"-"+action] = true | |
100 | } | 96 | } | |
101 | } | 97 | } | |
102 | 98 | |||
103 | // 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 | |
104 | // are collected to make the order of the definitions irrelevant. | 100 | // are collected to make the order of the definitions irrelevant. | |
101 | mklines.DetermineUsedVariables() | |||
105 | mklines.determineDefinedVariables() | 102 | mklines.determineDefinedVariables() | |
106 | 103 | |||
107 | // In the second pass, the actual checks are done. | 104 | // In the second pass, the actual checks are done. | |
108 | 105 | |||
109 | mklines.lines[0].CheckRcsid(`#\s+`, "# ") | 106 | mklines.lines[0].CheckRcsid(`#\s+`, "# ") | |
110 | 107 | |||
108 | substcontext := new(SubstContext) | |||
109 | varalign := new(VaralignBlock) | |||
111 | for _, mkline := range mklines.mklines { | 110 | for _, mkline := range mklines.mklines { | |
112 | mkline.Line.CheckTrailingWhitespace() | 111 | mkline.Line.CheckTrailingWhitespace() | |
113 | mkline.Line.CheckValidCharacters(`[\t -~]`) | 112 | mkline.Line.CheckValidCharacters(`[\t -~]`) | |
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.CheckVaralign() | |||
122 | mkline.CheckVarassign() | 121 | mkline.CheckVarassign() | |
123 | substcontext.Varassign(mkline) | 122 | substcontext.Varassign(mkline) | |
124 | 123 | |||
125 | case mkline.IsShellcmd(): | 124 | case mkline.IsShellcmd(): | |
126 | shellcmd := mkline.Shellcmd() | 125 | shellcmd := mkline.Shellcmd() | |
127 | mkline.CheckText(shellcmd) | 126 | mkline.CheckText(shellcmd) | |
128 | NewShellLine(mkline).CheckShellCommandLine(shellcmd) | 127 | NewShellLine(mkline).CheckShellCommandLine(shellcmd) | |
129 | 128 | |||
130 | case mkline.IsInclude(): | 129 | case mkline.IsInclude(): | |
131 | mklines.target = "" | 130 | mklines.target = "" | |
132 | mklines.checklineInclude(mkline) | 131 | mklines.checklineInclude(mkline) | |
133 | 132 | |||
134 | case mkline.IsCond(): | 133 | case mkline.IsCond(): | |
135 | mklines.checklineCond(mkline) | 134 | mklines.checklineCond(mkline) | |
136 | 135 | |||
137 | case mkline.IsDependency(): | 136 | case mkline.IsDependency(): | |
138 | mklines.checklineDependencyRule(mkline, mkline.Targets(), mkline.Sources(), allowedTargets) | 137 | mklines.checklineDependencyRule(mkline, mkline.Targets(), mkline.Sources(), allowedTargets) | |
139 | } | 138 | } | |
140 | } | 139 | } | |
141 | lastMkline := mklines.mklines[len(mklines.mklines)-1] | 140 | lastMkline := mklines.mklines[len(mklines.mklines)-1] | |
142 | substcontext.Finish(lastMkline) | 141 | substcontext.Finish(lastMkline) | |
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() { | |
@@ -369,13 +369,139 @@ func (mklines *MkLines) checklineInclude | @@ -369,13 +369,139 @@ func (mklines *MkLines) checklineInclude | |||
369 | if matches(includefile, `/x11-links/buildlink3\.mk$`) { | 369 | if matches(includefile, `/x11-links/buildlink3\.mk$`) { | |
370 | mkline.Error1("%s must not be included directly. Include \"../../mk/x11.buildlink3.mk\" instead.", includefile) | 370 | mkline.Error1("%s must not be included directly. Include \"../../mk/x11.buildlink3.mk\" instead.", includefile) | |
371 | } | 371 | } | |
372 | if matches(includefile, `/jpeg/buildlink3\.mk$`) { | 372 | if matches(includefile, `/jpeg/buildlink3\.mk$`) { | |
373 | mkline.Error1("%s must not be included directly. Include \"../../mk/jpeg.buildlink3.mk\" instead.", includefile) | 373 | mkline.Error1("%s must not be included directly. Include \"../../mk/jpeg.buildlink3.mk\" instead.", includefile) | |
374 | } | 374 | } | |
375 | if matches(includefile, `/intltool/buildlink3\.mk$`) { | 375 | if matches(includefile, `/intltool/buildlink3\.mk$`) { | |
376 | mkline.Warn0("Please write \"USE_TOOLS+= intltool\" instead of this line.") | 376 | mkline.Warn0("Please write \"USE_TOOLS+= intltool\" instead of this line.") | |
377 | } | 377 | } | |
378 | if m, dir := match1(includefile, `(.*)/builtin\.mk$`); m { | 378 | if m, dir := match1(includefile, `(.*)/builtin\.mk$`); m { | |
379 | mkline.Line.Error2("%s must not be included directly. Include \"%s/buildlink3.mk\" instead.", includefile, dir) | 379 | mkline.Line.Error2("%s must not be included directly. Include \"%s/buildlink3.mk\" instead.", includefile, dir) | |
380 | } | 380 | } | |
381 | } | 381 | } | |
382 | ||||
383 | type VaralignBlock struct { | |||
384 | info []struct { | |||
385 | mkline *MkLine | |||
386 | prefix string | |||
387 | align string | |||
388 | } | |||
389 | skip bool | |||
390 | differ bool | |||
391 | maxPrefixWidth int | |||
392 | maxSpaceWidth int | |||
393 | maxTabWidth int | |||
394 | } | |||
395 | ||||
396 | func (va *VaralignBlock) Check(mkline *MkLine) { | |||
397 | if !G.opts.WarnSpace || mkline.Line.IsMultiline() || mkline.IsComment() || mkline.IsCond() { | |||
398 | return | |||
399 | } | |||
400 | if mkline.IsEmpty() { | |||
401 | va.Finish() | |||
402 | return | |||
403 | } | |||
404 | if !mkline.IsVarassign() { | |||
405 | va.skip = true | |||
406 | return | |||
407 | } | |||
408 | ||||
409 | valueAlign := mkline.ValueAlign() | |||
410 | prefix := strings.TrimRight(valueAlign, " \t") | |||
411 | align := valueAlign[len(prefix):] | |||
412 | ||||
413 | va.info = append(va.info, struct { | |||
414 | mkline *MkLine | |||
415 | prefix string | |||
416 | align string | |||
417 | }{mkline, prefix, align}) | |||
418 | ||||
419 | alignedWidth := tabLength(valueAlign) | |||
420 | if contains(align, " ") { | |||
421 | if va.maxSpaceWidth != 0 && alignedWidth != va.maxSpaceWidth { | |||
422 | va.differ = true | |||
423 | } | |||
424 | if alignedWidth > va.maxSpaceWidth { | |||
425 | va.maxSpaceWidth = alignedWidth | |||
426 | } | |||
427 | } else { | |||
428 | if va.maxTabWidth != 0 && alignedWidth != va.maxTabWidth { | |||
429 | va.differ = true | |||
430 | } | |||
431 | if alignedWidth > va.maxTabWidth { | |||
432 | va.maxTabWidth = alignedWidth | |||
433 | } | |||
434 | } | |||
435 | ||||
436 | va.maxPrefixWidth = imax(va.maxPrefixWidth, tabLength(prefix)) | |||
437 | } | |||
438 | ||||
439 | func (va *VaralignBlock) Finish() { | |||
440 | if !va.skip { | |||
441 | for _, info := range va.info { | |||
442 | if !info.mkline.Line.IsMultiline() { | |||
443 | va.fixalign(info.mkline, info.prefix, info.align) | |||
444 | } | |||
445 | } | |||
446 | } | |||
447 | *va = VaralignBlock{} | |||
448 | } | |||
449 | ||||
450 | func (va *VaralignBlock) fixalign(mkline *MkLine, prefix, oldalign string) { | |||
451 | if mkline.Value() == "" && mkline.Comment() == "" { | |||
452 | return | |||
453 | } | |||
454 | ||||
455 | hasSpace := contains(oldalign, " ") | |||
456 | if hasSpace && | |||
457 | va.maxTabWidth != 0 && | |||
458 | va.maxSpaceWidth > va.maxTabWidth && | |||
459 | tabLength(prefix+oldalign) == va.maxSpaceWidth { | |||
460 | return | |||
461 | } | |||
462 | ||||
463 | goodWidth := va.maxTabWidth | |||
464 | if goodWidth == 0 && va.differ { | |||
465 | goodWidth = va.maxSpaceWidth | |||
466 | } | |||
467 | minWidth := va.maxPrefixWidth + 1 | |||
468 | if goodWidth == 0 || minWidth < goodWidth && va.differ { | |||
469 | goodWidth = minWidth | |||
470 | } | |||
471 | goodWidth = (goodWidth + 7) & -8 | |||
472 | ||||
473 | newalign := "" | |||
474 | for tabLength(prefix+newalign) < goodWidth { | |||
475 | newalign += "\t" | |||
476 | } | |||
477 | if newalign == oldalign { | |||
478 | return | |||
479 | } | |||
480 | ||||
481 | if !mkline.Line.AutofixReplace(prefix+oldalign, prefix+newalign) { | |||
482 | wrongColumn := tabLength(prefix+oldalign) != tabLength(prefix+newalign) | |||
483 | switch { | |||
484 | case hasSpace && wrongColumn: | |||
485 | mkline.Line.Notef("This variable value should be aligned with tabs, not spaces, to column %d.", goodWidth+1) | |||
486 | case hasSpace: | |||
487 | mkline.Line.Notef("Variable values should be aligned with tabs, not spaces.") | |||
488 | case wrongColumn: | |||
489 | mkline.Line.Notef("This variable value should be aligned to column %d.", goodWidth+1) | |||
490 | } | |||
491 | if wrongColumn { | |||
492 | Explain( | |||
493 | "Normally, all variable values in a block should start at the same", | |||
494 | "column. There are some exceptions to this rule:", | |||
495 | "", | |||
496 | "Definitions for long variable names may be indented with a single", | |||
497 | "space instead of tabs, but only if they appear in a block that is", | |||
498 | "otherwise indented using tabs.", | |||
499 | "", | |||
500 | "Variable definitions that span multiple lines are not checked for", | |||
501 | "alignment at all.", | |||
502 | "", | |||
503 | "When the block contains something else than variable definitions,", | |||
504 | "it is not checked at all.") | |||
505 | } | |||
506 | } | |||
507 | } |
@@ -75,54 +75,54 @@ func (s *Suite) TestChecklineRcsid(c *ch | @@ -75,54 +75,54 @@ func (s *Suite) TestChecklineRcsid(c *ch | |||
75 | "$"+"FreeBSD$") | 75 | "$"+"FreeBSD$") | |
76 | 76 | |||
77 | for _, line := range lines { | 77 | for _, line := range lines { | |
78 | line.CheckRcsid(``, "") | 78 | line.CheckRcsid(``, "") | |
79 | } | 79 | } | |
80 | 80 | |||
81 | c.Check(s.Output(), equals, ""+ | 81 | c.Check(s.Output(), equals, ""+ | |
82 | "ERROR: fname:3: Expected \"$"+"NetBSD$\".\n"+ | 82 | "ERROR: fname:3: Expected \"$"+"NetBSD$\".\n"+ | |
83 | "ERROR: fname:4: Expected \"$"+"NetBSD$\".\n"+ | 83 | "ERROR: fname:4: Expected \"$"+"NetBSD$\".\n"+ | |
84 | "ERROR: fname:5: Expected \"$"+"NetBSD$\".\n") | 84 | "ERROR: fname:5: Expected \"$"+"NetBSD$\".\n") | |
85 | } | 85 | } | |
86 | 86 | |||
87 | func (s *Suite) TestMatchVarassign(c *check.C) { | 87 | func (s *Suite) TestMatchVarassign(c *check.C) { | |
88 | checkVarassign := func(text string, ck check.Checker, varname, op, value, comment string) { | 88 | checkVarassign := func(text string, ck check.Checker, varname, op, align, value, comment string) { | |
89 | type va struct { | 89 | type va struct { | |
90 | varname, op, value, comment string | 90 | varname, op, align, value, comment string | |
91 | } | 91 | } | |
92 | expected := va{varname, op, value, comment} | 92 | expected := va{varname, op, align, value, comment} | |
93 | am, avarname, aop, avalue, acomment := MatchVarassign(text) | 93 | am, avarname, aop, aalign, avalue, acomment := MatchVarassign(text) | |
94 | if !am { | 94 | if !am { | |
95 | c.Errorf("Text %q doesn’t match variable assignment", text) | 95 | c.Errorf("Text %q doesn’t match variable assignment", text) | |
96 | return | 96 | return | |
97 | } | 97 | } | |
98 | actual := va{avarname, aop, avalue, acomment} | 98 | actual := va{avarname, aop, aalign, avalue, acomment} | |
99 | c.Check(actual, ck, expected) | 99 | c.Check(actual, ck, expected) | |
100 | } | 100 | } | |
101 | checkNotVarassign := func(text string) { | 101 | checkNotVarassign := func(text string) { | |
102 | m, _, _, _, _ := MatchVarassign(text) | 102 | m, _, _, _, _, _ := MatchVarassign(text) | |
103 | if m { | 103 | if m { | |
104 | c.Errorf("Text %q matches variable assignment, but shouldn’t.", text) | 104 | c.Errorf("Text %q matches variable assignment, but shouldn’t.", text) | |
105 | } | 105 | } | |
106 | } | 106 | } | |
107 | 107 | |||
108 | checkVarassign("C++=c11", equals, "C+", "+=", "c11", "") | 108 | checkVarassign("C++=c11", equals, "C+", "+=", "C++=", "c11", "") | |
109 | checkVarassign("V=v", equals, "V", "=", "v", "") | 109 | checkVarassign("V=v", equals, "V", "=", "V=", "v", "") | |
110 | checkVarassign("VAR=#comment", equals, "VAR", "=", "", "#comment") | 110 | checkVarassign("VAR=#comment", equals, "VAR", "=", "VAR=", "", "#comment") | |
111 | checkVarassign("VAR=\\#comment", equals, "VAR", "=", "#comment", "") | 111 | checkVarassign("VAR=\\#comment", equals, "VAR", "=", "VAR=", "#comment", "") | |
112 | checkVarassign("VAR=\\\\\\##comment", equals, "VAR", "=", "\\\\#", "#comment") | 112 | checkVarassign("VAR=\\\\\\##comment", equals, "VAR", "=", "VAR=", "\\\\#", "#comment") | |
113 | checkVarassign("VAR=\\", equals, "VAR", "=", "\\", "") | 113 | checkVarassign("VAR=\\", equals, "VAR", "=", "VAR=", "\\", "") | |
114 | checkVarassign("VAR += value", equals, "VAR", "+=", "value", "") | 114 | checkVarassign("VAR += value", equals, "VAR", "+=", "VAR += ", "value", "") | |
115 | checkVarassign(" VAR=value", equals, "VAR", "=", "value", "") | 115 | checkVarassign(" VAR=value", equals, "VAR", "=", " VAR=", "value", "") | |
116 | checkNotVarassign("\tVAR=value") | 116 | checkNotVarassign("\tVAR=value") | |
117 | checkNotVarassign("?=value") | 117 | checkNotVarassign("?=value") | |
118 | checkNotVarassign("<=value") | 118 | checkNotVarassign("<=value") | |
119 | } | 119 | } | |
120 | 120 | |||
121 | func (s *Suite) TestPackage_LoadPackageMakefile(c *check.C) { | 121 | func (s *Suite) TestPackage_LoadPackageMakefile(c *check.C) { | |
122 | makefile := s.CreateTmpFile(c, "category/package/Makefile", ""+ | 122 | makefile := s.CreateTmpFile(c, "category/package/Makefile", ""+ | |
123 | "# $"+"NetBSD$\n"+ | 123 | "# $"+"NetBSD$\n"+ | |
124 | "\n"+ | 124 | "\n"+ | |
125 | "PKGNAME=pkgname-1.67\n"+ | 125 | "PKGNAME=pkgname-1.67\n"+ | |
126 | "DISTNAME=distfile_1_67\n"+ | 126 | "DISTNAME=distfile_1_67\n"+ | |
127 | ".include \"../../category/package/Makefile\"\n") | 127 | ".include \"../../category/package/Makefile\"\n") | |
128 | pkg := NewPackage("category/package") | 128 | pkg := NewPackage("category/package") |