github.com/ajguerrer/rules_go@v0.20.3/go/tools/builders/importcfg.go (about) 1 // Copyright 2019 The Bazel Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "bufio" 19 "bytes" 20 "errors" 21 "fmt" 22 "io" 23 "io/ioutil" 24 "log" 25 "os" 26 "path/filepath" 27 "sort" 28 "strings" 29 ) 30 31 type archive struct { 32 label, importPath, packagePath, aFile, xFile string 33 importPathAliases []string 34 } 35 36 // checkImports verifies that each import in files refers to a 37 // direct dependendency in archives or to a standard library package 38 // listed in the file at stdPackageListPath. checkImports returns 39 // a map from source import paths to elements of archives or to nil 40 // for standard library packages. 41 func checkImports(files []fileInfo, archives []archive, stdPackageListPath string) (map[string]*archive, error) { 42 // Read the standard package list. 43 packagesTxt, err := ioutil.ReadFile(stdPackageListPath) 44 if err != nil { 45 return nil, err 46 } 47 stdPkgs := make(map[string]bool) 48 for len(packagesTxt) > 0 { 49 n := bytes.IndexByte(packagesTxt, '\n') 50 var line string 51 if n < 0 { 52 line = string(packagesTxt) 53 packagesTxt = nil 54 } else { 55 line = string(packagesTxt[:n]) 56 packagesTxt = packagesTxt[n+1:] 57 } 58 line = strings.TrimSpace(line) 59 if line == "" { 60 continue 61 } 62 stdPkgs[line] = true 63 } 64 65 // Index the archives. 66 importToArchive := make(map[string]*archive) 67 importAliasToArchive := make(map[string]*archive) 68 for i := range archives { 69 arc := &archives[i] 70 importToArchive[arc.importPath] = arc 71 for _, imp := range arc.importPathAliases { 72 importAliasToArchive[imp] = arc 73 } 74 } 75 76 // Build the import map. 77 imports := make(map[string]*archive) 78 var derr depsError 79 for _, f := range files { 80 for _, path := range f.imports { 81 if _, ok := imports[path]; ok || path == "C" || isRelative(path) { 82 // TODO(#1645): Support local (relative) import paths. We don't emit 83 // errors for them here, but they will probably break something else. 84 continue 85 } 86 if stdPkgs[path] { 87 imports[path] = nil 88 } else if arc := importToArchive[path]; arc != nil { 89 imports[path] = arc 90 } else if arc := importAliasToArchive[path]; arc != nil { 91 imports[path] = arc 92 } else { 93 derr.missing = append(derr.missing, missingDep{f.filename, path}) 94 } 95 } 96 } 97 if len(derr.missing) > 0 { 98 return nil, derr 99 } 100 return imports, nil 101 } 102 103 // buildImportcfgFileForCompile writes an importcfg file to be consumed by the 104 // compiler. The file is constructed from direct dependencies and std imports. 105 // The caller is responsible for deleting the importcfg file. 106 func buildImportcfgFileForCompile(imports map[string]*archive, installSuffix, dir string) (string, error) { 107 buf := &bytes.Buffer{} 108 goroot, ok := os.LookupEnv("GOROOT") 109 if !ok { 110 return "", errors.New("GOROOT not set") 111 } 112 goroot = abs(goroot) 113 114 sortedImports := make([]string, 0, len(imports)) 115 for imp := range imports { 116 sortedImports = append(sortedImports, imp) 117 } 118 sort.Strings(sortedImports) 119 120 for _, imp := range sortedImports { 121 if arc := imports[imp]; arc == nil { 122 // std package 123 path := filepath.Join(goroot, "pkg", installSuffix, filepath.FromSlash(imp)) 124 fmt.Fprintf(buf, "packagefile %s=%s.a\n", imp, path) 125 } else { 126 if imp != arc.packagePath { 127 fmt.Fprintf(buf, "importmap %s=%s\n", imp, arc.packagePath) 128 } 129 fmt.Fprintf(buf, "packagefile %s=%s\n", arc.packagePath, arc.aFile) 130 } 131 } 132 133 f, err := ioutil.TempFile(dir, "importcfg") 134 if err != nil { 135 return "", err 136 } 137 filename := f.Name() 138 if _, err := io.Copy(f, buf); err != nil { 139 f.Close() 140 os.Remove(filename) 141 return "", err 142 } 143 if err := f.Close(); err != nil { 144 os.Remove(filename) 145 return "", err 146 } 147 return filename, nil 148 } 149 150 func buildImportcfgFileForLink(archives []archive, stdPackageListPath, installSuffix, dir string) (string, error) { 151 buf := &bytes.Buffer{} 152 goroot, ok := os.LookupEnv("GOROOT") 153 if !ok { 154 return "", errors.New("GOROOT not set") 155 } 156 prefix := abs(filepath.Join(goroot, "pkg", installSuffix)) 157 stdPackageListFile, err := os.Open(stdPackageListPath) 158 if err != nil { 159 return "", err 160 } 161 defer stdPackageListFile.Close() 162 scanner := bufio.NewScanner(stdPackageListFile) 163 for scanner.Scan() { 164 line := strings.TrimSpace(scanner.Text()) 165 if line == "" { 166 continue 167 } 168 fmt.Fprintf(buf, "packagefile %s=%s.a\n", line, filepath.Join(prefix, filepath.FromSlash(line))) 169 } 170 if err := scanner.Err(); err != nil { 171 return "", err 172 } 173 depsSeen := map[string]string{} 174 for _, arc := range archives { 175 if conflictLabel, ok := depsSeen[arc.packagePath]; ok { 176 // TODO(#1327): link.bzl should report this as a failure after 0.11.0. 177 // At this point, we'll prepare an importcfg file and remove logic here. 178 log.Printf(`warning: package %q is provided by more than one rule: 179 %s 180 %s 181 Set "importmap" to different paths in each library. 182 This will be an error in the future.`, arc.packagePath, arc.label, conflictLabel) 183 continue 184 } 185 depsSeen[arc.packagePath] = arc.label 186 fmt.Fprintf(buf, "packagefile %s=%s\n", arc.packagePath, arc.aFile) 187 } 188 f, err := ioutil.TempFile(dir, "importcfg") 189 if err != nil { 190 return "", err 191 } 192 filename := f.Name() 193 if _, err := io.Copy(f, buf); err != nil { 194 f.Close() 195 os.Remove(filename) 196 return "", err 197 } 198 if err := f.Close(); err != nil { 199 os.Remove(filename) 200 return "", err 201 } 202 return filename, nil 203 } 204 205 type depsError struct { 206 missing []missingDep 207 known []string 208 } 209 210 type missingDep struct { 211 filename, imp string 212 } 213 214 var _ error = depsError{} 215 216 func (e depsError) Error() string { 217 buf := bytes.NewBuffer(nil) 218 fmt.Fprintf(buf, "missing strict dependencies:\n") 219 for _, dep := range e.missing { 220 fmt.Fprintf(buf, "\t%s: import of %q\n", dep.filename, dep.imp) 221 } 222 if len(e.known) == 0 { 223 fmt.Fprintln(buf, "No dependencies were provided.") 224 } else { 225 fmt.Fprintln(buf, "Known dependencies are:") 226 for _, imp := range e.known { 227 fmt.Fprintf(buf, "\t%s\n", imp) 228 } 229 } 230 fmt.Fprint(buf, "Check that imports in Go sources match importpath attributes in deps.") 231 return buf.String() 232 } 233 234 func isRelative(path string) bool { 235 return strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../") 236 } 237 238 // TODO(jayconrod): consolidate compile and link archive flags. 239 240 type compileArchiveMultiFlag []archive 241 242 func (m *compileArchiveMultiFlag) String() string { 243 if m == nil || len(*m) == 0 { 244 return "" 245 } 246 return fmt.Sprint(*m) 247 } 248 249 func (m *compileArchiveMultiFlag) Set(v string) error { 250 parts := strings.Split(v, "=") 251 if len(parts) != 4 { 252 return fmt.Errorf("badly formed -arc flag: %s", v) 253 } 254 importPaths := strings.Split(parts[0], ":") 255 a := archive{ 256 importPath: importPaths[0], 257 importPathAliases: importPaths[1:], 258 packagePath: parts[1], 259 aFile: abs(parts[2]), 260 } 261 if parts[3] != "" { 262 a.xFile = abs(parts[3]) 263 } 264 *m = append(*m, a) 265 return nil 266 } 267 268 type linkArchiveMultiFlag []archive 269 270 func (m *linkArchiveMultiFlag) String() string { 271 if m == nil || len(*m) == 0 { 272 return "" 273 } 274 return fmt.Sprint(m) 275 } 276 277 func (m *linkArchiveMultiFlag) Set(v string) error { 278 parts := strings.Split(v, "=") 279 if len(parts) != 3 { 280 return fmt.Errorf("badly formed -arc flag: %s", v) 281 } 282 *m = append(*m, archive{ 283 label: parts[0], 284 packagePath: parts[1], 285 aFile: abs(parts[2]), 286 }) 287 return nil 288 }