github.com/0xKiwi/rules_go@v0.24.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 "os" 25 "path/filepath" 26 "sort" 27 "strings" 28 ) 29 30 type archive struct { 31 label, importPath, packagePath, file string 32 importPathAliases []string 33 } 34 35 // checkImports verifies that each import in files refers to a 36 // direct dependendency in archives or to a standard library package 37 // listed in the file at stdPackageListPath. checkImports returns 38 // a map from source import paths to elements of archives or to nil 39 // for standard library packages. 40 func checkImports(files []fileInfo, archives []archive, stdPackageListPath string) (map[string]*archive, error) { 41 // Read the standard package list. 42 packagesTxt, err := ioutil.ReadFile(stdPackageListPath) 43 if err != nil { 44 return nil, err 45 } 46 stdPkgs := make(map[string]bool) 47 for len(packagesTxt) > 0 { 48 n := bytes.IndexByte(packagesTxt, '\n') 49 var line string 50 if n < 0 { 51 line = string(packagesTxt) 52 packagesTxt = nil 53 } else { 54 line = string(packagesTxt[:n]) 55 packagesTxt = packagesTxt[n+1:] 56 } 57 line = strings.TrimSpace(line) 58 if line == "" { 59 continue 60 } 61 stdPkgs[line] = true 62 } 63 64 // Index the archives. 65 importToArchive := make(map[string]*archive) 66 importAliasToArchive := make(map[string]*archive) 67 for i := range archives { 68 arc := &archives[i] 69 importToArchive[arc.importPath] = arc 70 for _, imp := range arc.importPathAliases { 71 importAliasToArchive[imp] = arc 72 } 73 } 74 75 // Build the import map. 76 imports := make(map[string]*archive) 77 var derr depsError 78 for _, f := range files { 79 for _, path := range f.imports { 80 if _, ok := imports[path]; ok || path == "C" || isRelative(path) { 81 // TODO(#1645): Support local (relative) import paths. We don't emit 82 // errors for them here, but they will probably break something else. 83 continue 84 } 85 if stdPkgs[path] { 86 imports[path] = nil 87 } else if arc := importToArchive[path]; arc != nil { 88 imports[path] = arc 89 } else if arc := importAliasToArchive[path]; arc != nil { 90 imports[path] = arc 91 } else { 92 derr.missing = append(derr.missing, missingDep{f.filename, path}) 93 } 94 } 95 } 96 if len(derr.missing) > 0 { 97 return nil, derr 98 } 99 return imports, nil 100 } 101 102 // buildImportcfgFileForCompile writes an importcfg file to be consumed by the 103 // compiler. The file is constructed from direct dependencies and std imports. 104 // The caller is responsible for deleting the importcfg file. 105 func buildImportcfgFileForCompile(imports map[string]*archive, installSuffix, dir string) (string, error) { 106 buf := &bytes.Buffer{} 107 goroot, ok := os.LookupEnv("GOROOT") 108 if !ok { 109 return "", errors.New("GOROOT not set") 110 } 111 goroot = abs(goroot) 112 113 sortedImports := make([]string, 0, len(imports)) 114 for imp := range imports { 115 sortedImports = append(sortedImports, imp) 116 } 117 sort.Strings(sortedImports) 118 119 for _, imp := range sortedImports { 120 if arc := imports[imp]; arc == nil { 121 // std package 122 path := filepath.Join(goroot, "pkg", installSuffix, filepath.FromSlash(imp)) 123 fmt.Fprintf(buf, "packagefile %s=%s.a\n", imp, path) 124 } else { 125 if imp != arc.packagePath { 126 fmt.Fprintf(buf, "importmap %s=%s\n", imp, arc.packagePath) 127 } 128 fmt.Fprintf(buf, "packagefile %s=%s\n", arc.packagePath, arc.file) 129 } 130 } 131 132 f, err := ioutil.TempFile(dir, "importcfg") 133 if err != nil { 134 return "", err 135 } 136 filename := f.Name() 137 if _, err := io.Copy(f, buf); err != nil { 138 f.Close() 139 os.Remove(filename) 140 return "", err 141 } 142 if err := f.Close(); err != nil { 143 os.Remove(filename) 144 return "", err 145 } 146 return filename, nil 147 } 148 149 func buildImportcfgFileForLink(archives []archive, stdPackageListPath, installSuffix, dir string) (string, error) { 150 buf := &bytes.Buffer{} 151 goroot, ok := os.LookupEnv("GOROOT") 152 if !ok { 153 return "", errors.New("GOROOT not set") 154 } 155 prefix := abs(filepath.Join(goroot, "pkg", installSuffix)) 156 stdPackageListFile, err := os.Open(stdPackageListPath) 157 if err != nil { 158 return "", err 159 } 160 defer stdPackageListFile.Close() 161 scanner := bufio.NewScanner(stdPackageListFile) 162 for scanner.Scan() { 163 line := strings.TrimSpace(scanner.Text()) 164 if line == "" { 165 continue 166 } 167 fmt.Fprintf(buf, "packagefile %s=%s.a\n", line, filepath.Join(prefix, filepath.FromSlash(line))) 168 } 169 if err := scanner.Err(); err != nil { 170 return "", err 171 } 172 depsSeen := map[string]string{} 173 for _, arc := range archives { 174 if _, ok := depsSeen[arc.packagePath]; ok { 175 // If this is detected during analysis, the -conflict_err flag will be set. 176 // We'll report that error if -package_conflict_is_error is set or if 177 // the link command fails. 178 // TODO(#1374): This should always be an error. Panic. 179 continue 180 } 181 depsSeen[arc.packagePath] = arc.label 182 fmt.Fprintf(buf, "packagefile %s=%s\n", arc.packagePath, arc.file) 183 } 184 f, err := ioutil.TempFile(dir, "importcfg") 185 if err != nil { 186 return "", err 187 } 188 filename := f.Name() 189 if _, err := io.Copy(f, buf); err != nil { 190 f.Close() 191 os.Remove(filename) 192 return "", err 193 } 194 if err := f.Close(); err != nil { 195 os.Remove(filename) 196 return "", err 197 } 198 return filename, nil 199 } 200 201 type depsError struct { 202 missing []missingDep 203 known []string 204 } 205 206 type missingDep struct { 207 filename, imp string 208 } 209 210 var _ error = depsError{} 211 212 func (e depsError) Error() string { 213 buf := bytes.NewBuffer(nil) 214 fmt.Fprintf(buf, "missing strict dependencies:\n") 215 for _, dep := range e.missing { 216 fmt.Fprintf(buf, "\t%s: import of %q\n", dep.filename, dep.imp) 217 } 218 if len(e.known) == 0 { 219 fmt.Fprintln(buf, "No dependencies were provided.") 220 } else { 221 fmt.Fprintln(buf, "Known dependencies are:") 222 for _, imp := range e.known { 223 fmt.Fprintf(buf, "\t%s\n", imp) 224 } 225 } 226 fmt.Fprint(buf, "Check that imports in Go sources match importpath attributes in deps.") 227 return buf.String() 228 } 229 230 func isRelative(path string) bool { 231 return strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../") 232 } 233 234 type archiveMultiFlag []archive 235 236 func (m *archiveMultiFlag) String() string { 237 if m == nil || len(*m) == 0 { 238 return "" 239 } 240 return fmt.Sprint(*m) 241 } 242 243 func (m *archiveMultiFlag) Set(v string) error { 244 parts := strings.Split(v, "=") 245 if len(parts) != 3 { 246 return fmt.Errorf("badly formed -arc flag: %s", v) 247 } 248 importPaths := strings.Split(parts[0], ":") 249 a := archive{ 250 importPath: importPaths[0], 251 importPathAliases: importPaths[1:], 252 packagePath: parts[1], 253 file: abs(parts[2]), 254 } 255 *m = append(*m, a) 256 return nil 257 }