Annotation of pkgsrc/pkgtools/pkglint/files/category.go, Revision 1.22
1.16 rillig 1: package pkglint
1.1 rillig 2:
1.19 rillig 3: import (
4: "fmt"
5: "netbsd.org/pkglint/textproc"
6: "strings"
7: )
1.1 rillig 8:
1.13 rillig 9: func CheckdirCategory(dir string) {
1.7 rillig 10: if trace.Tracing {
1.13 rillig 11: defer trace.Call1(dir)()
1.4 rillig 12: }
1.1 rillig 13:
1.14 rillig 14: mklines := LoadMk(dir+"/Makefile", NotEmpty|LogErrors)
15: if mklines == nil {
1.1 rillig 16: return
17: }
1.4 rillig 18:
19: mklines.Check()
1.1 rillig 20:
1.17 rillig 21: mlex := NewMkLinesLexer(mklines)
22: for mlex.SkipPrefix("#") {
1.1 rillig 23: }
1.17 rillig 24: mlex.SkipEmptyOrNote()
1.15 rillig 25:
1.20 rillig 26: if mlex.SkipIf(func(mkline *MkLine) bool { return mkline.IsVarassign() && mkline.Varname() == "COMMENT" }) {
1.17 rillig 27: mkline := mlex.PreviousMkLine()
1.15 rillig 28:
29: lex := textproc.NewLexer(mkline.Value())
30: valid := textproc.NewByteSet("--- '(),/0-9A-Za-z")
31: invalid := valid.Inverse()
1.19 rillig 32: var uni strings.Builder
1.15 rillig 33:
34: for !lex.EOF() {
35: _ = lex.NextBytesSet(valid)
36: ch := lex.NextByteSet(invalid)
37: if ch != -1 {
1.19 rillig 38: _, _ = fmt.Fprintf(&uni, " %U", ch)
1.15 rillig 39: }
40: }
41:
1.19 rillig 42: if uni.Len() > 0 {
43: mkline.Warnf("%s contains invalid characters (%s).", mkline.Varname(), uni.String()[1:])
1.15 rillig 44: }
1.1 rillig 45:
1.4 rillig 46: } else {
1.17 rillig 47: mlex.CurrentLine().Errorf("COMMENT= line expected.")
1.1 rillig 48: }
1.17 rillig 49: mlex.SkipEmptyOrNote()
1.1 rillig 50:
1.4 rillig 51: type subdir struct {
1.15 rillig 52: name string
1.20 rillig 53: line *MkLine
1.1 rillig 54: }
55:
56: // And now to the most complicated part of the category Makefiles,
57: // the (hopefully) sorted list of SUBDIRs. The first step is to
58: // collect the SUBDIRs in the Makefile and in the file system.
59:
1.13 rillig 60: fSubdirs := getSubdirs(dir)
1.2 rillig 61: var mSubdirs []subdir
1.1 rillig 62:
1.20 rillig 63: seen := make(map[string]*MkLine)
1.17 rillig 64: for !mlex.EOF() {
65: mkline := mlex.CurrentMkLine()
1.1 rillig 66:
1.21 rillig 67: if (mkline.IsVarassignMaybeCommented()) && mkline.Varname() == "SUBDIR" {
1.17 rillig 68: mlex.Skip()
1.15 rillig 69:
70: name := mkline.Value()
71: if mkline.IsCommentedVarassign() && mkline.VarassignComment() == "" {
72: mkline.Warnf("%q commented out without giving a reason.", name)
1.1 rillig 73: }
74:
1.15 rillig 75: if prev := seen[name]; prev != nil {
76: mkline.Errorf("%q must only appear once, already seen in %s.", name, mkline.RefTo(prev))
1.1 rillig 77: }
1.15 rillig 78: seen[name] = mkline
1.1 rillig 79:
1.15 rillig 80: if len(mSubdirs) > 0 {
81: if prev := mSubdirs[len(mSubdirs)-1].name; name < prev {
82: mkline.Warnf("%q should come before %q.", name, prev)
83: }
1.1 rillig 84: }
85:
1.15 rillig 86: mSubdirs = append(mSubdirs, subdir{name, mkline})
1.1 rillig 87:
88: } else {
1.15 rillig 89: if !mkline.IsEmpty() {
90: mkline.Errorf("SUBDIR+= line or empty line expected.")
1.1 rillig 91: }
92: break
93: }
94: }
95:
96: // To prevent unnecessary warnings about subdirectories that are
1.15 rillig 97: // in one list but not in the other, generate the sets of
1.1 rillig 98: // subdirs of each list.
99: fCheck := make(map[string]bool)
100: mCheck := make(map[string]bool)
101: for _, fsub := range fSubdirs {
102: fCheck[fsub] = true
103: }
104: for _, msub := range mSubdirs {
1.2 rillig 105: mCheck[msub.name] = true
1.1 rillig 106: }
107:
1.15 rillig 108: fRest := fSubdirs[:]
109: mRest := mSubdirs[:]
1.1 rillig 110:
1.15 rillig 111: for len(mRest) > 0 || len(fRest) > 0 {
1.1 rillig 112:
1.15 rillig 113: if len(fRest) > 0 && (len(mRest) == 0 || fRest[0] < mRest[0].name) {
114: fCurrent := fRest[0]
115: if !mCheck[fCurrent] {
1.20 rillig 116: var line *Line
1.15 rillig 117: if len(mRest) > 0 {
118: line = mRest[0].line.Line
119: } else {
1.17 rillig 120: line = mlex.CurrentLine()
1.15 rillig 121: }
1.1 rillig 122:
1.10 rillig 123: fix := line.Autofix()
1.15 rillig 124: fix.Errorf("%q exists in the file system but not in the Makefile.", fCurrent)
1.10 rillig 125: fix.InsertBefore("SUBDIR+=\t" + fCurrent)
126: fix.Apply()
1.2 rillig 127: }
1.15 rillig 128: fRest = fRest[1:]
1.2 rillig 129:
1.18 rillig 130: } else if len(fRest) == 0 || mRest[0].name < fRest[0] {
1.15 rillig 131: if !fCheck[mRest[0].name] {
132: fix := mRest[0].line.Autofix()
133: fix.Errorf("%q exists in the Makefile but not in the file system.", mRest[0].name)
1.10 rillig 134: fix.Delete()
135: fix.Apply()
1.1 rillig 136: }
1.15 rillig 137: mRest = mRest[1:]
1.1 rillig 138:
1.15 rillig 139: } else {
140: fRest = fRest[1:]
141: mRest = mRest[1:]
1.1 rillig 142: }
143: }
144:
145: // the pkgsrc-wip category Makefile defines its own targets for
146: // generating indexes and READMEs. Just skip them.
1.15 rillig 147: if !G.Wip {
1.17 rillig 148: mlex.SkipEmptyOrNote()
149: mlex.SkipContainsOrWarn(".include \"../mk/misc/category.mk\"")
150: if !mlex.EOF() {
151: mlex.CurrentLine().Errorf("The file should end here.")
1.15 rillig 152: }
1.1 rillig 153: }
154:
1.14 rillig 155: mklines.SaveAutofixChanges()
1.1 rillig 156:
1.15 rillig 157: if G.Opts.Recursive {
158: var recurseInto []string
159: for _, msub := range mSubdirs {
160: if !msub.line.IsCommentedVarassign() {
1.22 ! rillig 161: recurseInto = append(recurseInto, joinPath(dir, msub.name))
1.15 rillig 162: }
163: }
164: G.Todo = append(recurseInto, G.Todo...)
1.1 rillig 165: }
166: }
CVSweb <webmaster@jp.NetBSD.org>