-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathprojectlayout.go
More file actions
126 lines (109 loc) · 3.63 KB
/
projectlayout.go
File metadata and controls
126 lines (109 loc) · 3.63 KB
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
115
116
117
118
119
120
121
122
123
124
125
126
package srcarchive
import (
"bufio"
"errors"
"fmt"
"os"
"strings"
"github.com/github/codeql-go/extractor/util"
)
// ProjectLayout describes a very simple project layout rewriting paths starting
// with `from` to start with `to` instead.
//
// We currently only support project layouts of the form:
//
// # to
// from//
type ProjectLayout struct {
From, To string
}
// normaliseSlashes adds an initial slash to `path` if there isn't one, and trims
// a final slash if there is one
func normaliseSlashes(path string) string {
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
return strings.TrimSuffix(path, "/")
}
// LoadProjectLayoutFromEnv loads a project layout from the file referenced by the
// {CODEQL,SEMMLE}_PATH_TRANSFORMER environment variable. If neither env var is set, returns nil. If
// the file cannot be read or does not have the right format, it returns an error.
func LoadProjectLayoutFromEnv() (*ProjectLayout, error) {
pt := util.Getenv("CODEQL_PATH_TRANSFORMER", "SEMMLE_PATH_TRANSFORMER")
if pt == "" {
return nil, nil
}
ptf, err := os.Open(pt)
if err != nil {
return nil, err
}
projLayout, err := LoadProjectLayout(ptf)
if err != nil {
return nil, err
}
return projLayout, nil
}
// LoadProjectLayout loads a project layout from the given file, returning an error
// if the file does not have the right format
func LoadProjectLayout(file *os.File) (*ProjectLayout, error) {
res := ProjectLayout{}
scanner := bufio.NewScanner(file)
line := ""
for ; line == "" && scanner.Scan(); line = strings.TrimSpace(scanner.Text()) {
}
if !strings.HasPrefix(line, "#") {
return nil, fmt.Errorf("first line of project layout should start with #, but got %s", line)
}
res.To = normaliseSlashes(strings.TrimSpace(strings.TrimPrefix(line, "#")))
if !scanner.Scan() {
return nil, errors.New("empty section in project-layout file")
}
line = strings.TrimSpace(scanner.Text())
if !strings.HasSuffix(line, "//") {
return nil, errors.New("unsupported project-layout feature")
}
line = strings.TrimSuffix(line, "//")
if strings.HasPrefix(line, "-") || strings.Contains(line, "*") || strings.Contains(line, "//") {
return nil, errors.New("unsupported project-layout feature")
}
res.From = normaliseSlashes(line)
for scanner.Scan() {
if strings.TrimSpace(scanner.Text()) != "" {
return nil, errors.New("only one section with one rewrite supported")
}
}
return &res, nil
}
// transformString transforms `str` as specified by the project layout: if it starts with the `from`
// prefix, that prefix is relaced by `to`; otherwise the string is returned unchanged
func (p *ProjectLayout) transformString(str string) string {
if str == p.From {
return p.To
}
if strings.HasPrefix(str, p.From+"/") {
return p.To + "/" + str[len(p.From)+1:]
}
return str
}
// isWindowsPath checks whether the substring of `path` starting at `idx` looks like a (slashified)
// Windows path, that is, starts with a drive letter followed by a colon and a slash
func isWindowsPath(path string, idx int) bool {
return len(path) >= 3+idx &&
path[idx] != '/' &&
path[idx+1] == ':' && path[idx+2] == '/'
}
// Transform transforms the given path according to the project layout: if it starts with the `from`
// prefix, that prefix is relaced by `to`; otherwise the path is returned unchanged.
//
// Unlike the (internal) method `transformString`, this method handles Windows paths sensibly.
func (p *ProjectLayout) Transform(path string) string {
if isWindowsPath(path, 0) {
result := p.transformString("/" + path)
if isWindowsPath(result, 1) && result[0] == '/' {
return result[1:]
}
return result
} else {
return p.transformString(path)
}
}