...

Source file src/github.com/bazelbuild/rules_go/go/tools/builders/importcfg.go

Documentation: github.com/bazelbuild/rules_go/go/tools/builders

     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 dependency 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, importPath string, recompileInternalDeps []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  	// Construct recompileInternalDeps as a map to check if there are imports that are disallowed.
    75  	recompileInternalDepMap := make(map[string]struct{})
    76  	for _, dep := range recompileInternalDeps {
    77  		recompileInternalDepMap[dep] = struct{}{}
    78  	}
    79  	// Build the import map.
    80  	imports := make(map[string]*archive)
    81  	var derr depsError
    82  	for _, f := range files {
    83  		for _, imp := range f.imports {
    84  			path := imp.path
    85  			if _, ok := imports[path]; ok || path == "C" || isRelative(path) {
    86  				// TODO(#1645): Support local (relative) import paths. We don't emit
    87  				// errors for them here, but they will probably break something else.
    88  				continue
    89  			}
    90  			if _, ok := recompileInternalDepMap[path]; ok {
    91  				return nil, fmt.Errorf("dependency cycle detected between %q and %q in file %q", importPath, path, f.filename)
    92  			}
    93  			if stdPkgs[path] {
    94  				imports[path] = nil
    95  			} else if arc := importToArchive[path]; arc != nil {
    96  				imports[path] = arc
    97  			} else if arc := importAliasToArchive[path]; arc != nil {
    98  				imports[path] = arc
    99  			} else {
   100  				derr.missing = append(derr.missing, missingDep{f.filename, path})
   101  			}
   102  		}
   103  	}
   104  	if len(derr.missing) > 0 {
   105  		return nil, derr
   106  	}
   107  	return imports, nil
   108  }
   109  
   110  // buildImportcfgFileForCompile writes an importcfg file to be consumed by the
   111  // compiler. The file is constructed from direct dependencies and std imports.
   112  // The caller is responsible for deleting the importcfg file.
   113  func buildImportcfgFileForCompile(imports map[string]*archive, installSuffix, dir string) (string, error) {
   114  	buf := &bytes.Buffer{}
   115  	goroot, ok := os.LookupEnv("GOROOT")
   116  	if !ok {
   117  		return "", errors.New("GOROOT not set")
   118  	}
   119  	goroot = abs(goroot)
   120  
   121  	sortedImports := make([]string, 0, len(imports))
   122  	for imp := range imports {
   123  		sortedImports = append(sortedImports, imp)
   124  	}
   125  	sort.Strings(sortedImports)
   126  
   127  	for _, imp := range sortedImports {
   128  		if arc := imports[imp]; arc == nil {
   129  			// std package
   130  			path := filepath.Join(goroot, "pkg", installSuffix, filepath.FromSlash(imp))
   131  			fmt.Fprintf(buf, "packagefile %s=%s.a\n", imp, path)
   132  		} else {
   133  			if imp != arc.packagePath {
   134  				fmt.Fprintf(buf, "importmap %s=%s\n", imp, arc.packagePath)
   135  			}
   136  			fmt.Fprintf(buf, "packagefile %s=%s\n", arc.packagePath, arc.file)
   137  		}
   138  	}
   139  
   140  	f, err := ioutil.TempFile(dir, "importcfg")
   141  	if err != nil {
   142  		return "", err
   143  	}
   144  	filename := f.Name()
   145  	if _, err := io.Copy(f, buf); err != nil {
   146  		f.Close()
   147  		os.Remove(filename)
   148  		return "", err
   149  	}
   150  	if err := f.Close(); err != nil {
   151  		os.Remove(filename)
   152  		return "", err
   153  	}
   154  	return filename, nil
   155  }
   156  
   157  func buildImportcfgFileForLink(archives []archive, stdPackageListPath, installSuffix, dir string) (string, error) {
   158  	buf := &bytes.Buffer{}
   159  	goroot, ok := os.LookupEnv("GOROOT")
   160  	if !ok {
   161  		return "", errors.New("GOROOT not set")
   162  	}
   163  	prefix := abs(filepath.Join(goroot, "pkg", installSuffix))
   164  	stdPackageListFile, err := os.Open(stdPackageListPath)
   165  	if err != nil {
   166  		return "", err
   167  	}
   168  	defer stdPackageListFile.Close()
   169  	scanner := bufio.NewScanner(stdPackageListFile)
   170  	for scanner.Scan() {
   171  		line := strings.TrimSpace(scanner.Text())
   172  		if line == "" {
   173  			continue
   174  		}
   175  		fmt.Fprintf(buf, "packagefile %s=%s.a\n", line, filepath.Join(prefix, filepath.FromSlash(line)))
   176  	}
   177  	if err := scanner.Err(); err != nil {
   178  		return "", err
   179  	}
   180  	depsSeen := map[string]string{}
   181  	for _, arc := range archives {
   182  		if _, ok := depsSeen[arc.packagePath]; ok {
   183  			return "", fmt.Errorf("internal error: package %s provided multiple times. This should have been detected during analysis.", arc.packagePath)
   184  		}
   185  		depsSeen[arc.packagePath] = arc.label
   186  		fmt.Fprintf(buf, "packagefile %s=%s\n", arc.packagePath, arc.file)
   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  type archiveMultiFlag []archive
   239  
   240  func (m *archiveMultiFlag) String() string {
   241  	if m == nil || len(*m) == 0 {
   242  		return ""
   243  	}
   244  	return fmt.Sprint(*m)
   245  }
   246  
   247  func (m *archiveMultiFlag) Set(v string) error {
   248  	parts := strings.Split(v, "=")
   249  	if len(parts) != 3 {
   250  		return fmt.Errorf("badly formed -arc flag: %s", v)
   251  	}
   252  	importPaths := strings.Split(parts[0], ":")
   253  	a := archive{
   254  		importPath:        importPaths[0],
   255  		importPathAliases: importPaths[1:],
   256  		packagePath:       parts[1],
   257  		file:              abs(parts[2]),
   258  	}
   259  	*m = append(*m, a)
   260  	return nil
   261  }
   262  

View as plain text