1
16
17
18 package wspace
19
20 import (
21 "io/ioutil"
22 "os"
23 "path/filepath"
24 "sort"
25 "strings"
26
27 "github.com/bazelbuild/buildtools/build"
28 )
29
30 const workspaceFile = "WORKSPACE"
31 const buildFile = "BUILD"
32
33
34
35 func IsRegularFile(path string) bool {
36 path, err := filepath.EvalSymlinks(path)
37 if err != nil {
38 return false
39 }
40 info, err := os.Stat(path)
41 if err != nil {
42 return false
43 }
44 return info.Mode().IsRegular()
45 }
46
47 func isFile(fi os.FileInfo) bool {
48 return fi.Mode()&os.ModeType == 0
49 }
50
51 func isExecutable(fi os.FileInfo) bool {
52 return isFile(fi) && fi.Mode()&0100 == 0100
53 }
54
55 var repoRootFiles = map[string]func(os.FileInfo) bool{
56 workspaceFile: isFile,
57 workspaceFile + ".bazel": isFile,
58 ".buckconfig": isFile,
59 "pants": isExecutable,
60 }
61
62 var packageRootFiles = map[string]func(os.FileInfo) bool{
63 buildFile: isFile,
64 buildFile + ".bazel": isFile,
65 }
66
67
68 func findContextPath(rootDir string) (string, error) {
69 if rootDir == "" {
70 return os.Getwd()
71 }
72 return rootDir, nil
73 }
74
75
76
77
78 func FindWorkspaceRoot(rootDir string) (root string, rest string) {
79 wd, err := findContextPath(rootDir)
80 if err != nil {
81 return "", ""
82 }
83 if root, err = Find(wd, repoRootFiles); err != nil {
84 return "", ""
85 }
86 if len(wd) == len(root) {
87 return root, ""
88 }
89 return root, wd[len(root)+1:]
90 }
91
92
93
94 func Find(dir string, rootFiles map[string]func(os.FileInfo) bool) (string, error) {
95 if dir == "" || dir == "/" || dir == "." || (len(dir) == 3 && strings.HasSuffix(dir, ":\\")) {
96 return "", os.ErrNotExist
97 }
98
99
100 rootFilesSorted := make([]string, 0, len(rootFiles))
101 for file := range rootFiles {
102 rootFilesSorted = append(rootFilesSorted, file)
103 }
104 sort.Strings(rootFilesSorted)
105
106 for _, repoRootFile := range rootFilesSorted {
107 fiFunc := rootFiles[repoRootFile]
108 if fi, err := os.Stat(filepath.Join(dir, repoRootFile)); err == nil && fiFunc(fi) {
109 return dir, nil
110 } else if err != nil && !os.IsNotExist(err) {
111 return "", err
112 }
113 }
114 return Find(filepath.Dir(dir), rootFiles)
115 }
116
117
118
119
120
121 func FindRepoBuildFiles(root string) (map[string]string, error) {
122 ws := filepath.Join(root, workspaceFile)
123 kinds := []string{
124 "git_repository",
125 "new_local_repository",
126 "new_git_repository",
127 "new_http_archive",
128 }
129 data, err := ioutil.ReadFile(ws)
130 if err != nil {
131 return nil, err
132 }
133 ast, err := build.Parse(ws, data)
134 if err != nil {
135 return nil, err
136 }
137 files := make(map[string]string)
138 for _, kind := range kinds {
139 for _, r := range ast.Rules(kind) {
140 buildFile := r.AttrString("build_file")
141 if buildFile == "" {
142 continue
143 }
144 buildFile = strings.Replace(buildFile, ":", "/", -1)
145 files[r.Name()] = filepath.Join(root, buildFile)
146 }
147 }
148 return files, nil
149 }
150
151
152
153 func relPath(base, target string) (string, error) {
154 rel, err := filepath.Rel(base, target)
155 if err != nil {
156 return "", err
157 }
158 if rel == "." {
159 return "", nil
160 }
161 return strings.ReplaceAll(rel, string(os.PathSeparator), "/"), nil
162 }
163
164
165
166
167
168
169 func SplitFilePath(filename string) (workspaceRoot, pkg, label string) {
170 dir := filepath.Dir(filename)
171 workspaceRoot, err := Find(dir, repoRootFiles)
172 if err != nil {
173 return "", "", ""
174 }
175 packageRoot, err := Find(dir, packageRootFiles)
176 if err != nil || !strings.HasPrefix(packageRoot, workspaceRoot) {
177
178
179 packageRoot = workspaceRoot
180 }
181 pkg, err = relPath(workspaceRoot, packageRoot)
182 if err != nil {
183 return "", "", ""
184 }
185 label, err = relPath(packageRoot, filename)
186 if err != nil {
187 return "", "", ""
188 }
189 return workspaceRoot, pkg, label
190 }
191
View as plain text