/
alternatives.go
114 lines (93 loc) · 3.13 KB
/
alternatives.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package pkglint
import (
"netbsd.org/pkglint/textproc"
"strings"
)
func CheckFileAlternatives(filename CurrPath, pkg *Package) {
lines := Load(filename, NotEmpty|LogErrors)
if lines == nil {
return
}
var ck AlternativesChecker
ck.Check(lines, pkg)
}
type AlternativesChecker struct{}
func (ck *AlternativesChecker) Check(lines *Lines, pkg *Package) {
var plistFiles map[RelPath]*PlistLine
if pkg != nil {
plistFiles = pkg.Plist.Files
}
for _, line := range lines.Lines {
ck.checkLine(line, plistFiles, pkg)
}
}
// checkLine checks a single line for the following format:
// wrapper alternative [optional arguments]
func (ck *AlternativesChecker) checkLine(line *Line, plistFiles map[RelPath]*PlistLine, pkg *Package) {
m, wrapper, space, alternative := match3(line.Text, `^([^\t ]+)([ \t]+)([^\t ]+).*$`)
if !m {
line.Errorf("Invalid line %q.", line.Text)
line.Explain(
sprintf("Run %q for more information.", bmakeHelp("alternatives")))
return
}
if ck.checkWrapperAbs(line, NewPath(wrapper)) && plistFiles != nil {
ck.checkWrapperPlist(line, NewRelPathString(wrapper), plistFiles)
}
if plistFiles != nil {
ck.checkAlternativePlist(line, alternative, plistFiles, pkg)
}
ck.checkAlternativeAbs(alternative, line, space)
LineChecker{line}.CheckTrailingWhitespace()
}
func (ck *AlternativesChecker) checkWrapperAbs(line *Line, wrapper Path) bool {
if !wrapper.IsAbs() {
return true
}
line.Errorf("Alternative wrapper %q must be relative to PREFIX.", wrapper.String())
return false
}
func (ck *AlternativesChecker) checkWrapperPlist(line *Line, wrapper RelPath,
plistFiles map[RelPath]*PlistLine) {
if plistFiles[wrapper] != nil {
line.Errorf("Alternative wrapper %q must not appear in the PLIST.", wrapper)
}
}
func (ck *AlternativesChecker) checkAlternativeAbs(alternative string, line *Line, space string) {
lex := textproc.NewLexer(alternative)
if lex.SkipByte('/') || lex.SkipByte('@') {
return
}
fix := line.Autofix()
fix.Errorf("Alternative implementation %q must be an absolute path.", alternative)
fix.Explain(
"It usually starts with @PREFIX@/... to refer to a path inside the installation prefix.")
if lex.TestByteSet(textproc.Alnum) {
fix.ReplaceAfter(space, alternative, "@PREFIX@/"+alternative)
}
fix.Apply()
}
func (ck *AlternativesChecker) checkAlternativePlist(line *Line, alternative string,
plistFiles map[RelPath]*PlistLine, pkg *Package) {
relImplementation := strings.Replace(alternative, "@PREFIX@/", "", 1)
plistName := replaceAll(relImplementation, `@(\w+)@`, "${$1}")
if NewPath(plistName).IsAbs() {
// It's possible but unusual to refer to a fixed absolute path.
// These cannot be mentioned in the PLIST since they are not part of the package.
return
}
rel := NewRelPathString(plistName)
if plistFiles[rel] != nil || pkg.vars.IsDefined("ALTERNATIVES_SRC") {
return
}
if plistFiles[rel.Replace("${PKGMANDIR}", "man")] != nil {
return
}
if plistName == alternative {
line.Errorf("Alternative implementation %q must appear in the PLIST.",
alternative)
} else {
line.Errorf("Alternative implementation %q must appear in the PLIST as %q.",
alternative, plistName)
}
}