diff --git a/go/private/actions/archive.bzl b/go/private/actions/archive.bzl index db61006804..2ae59c1a02 100644 --- a/go/private/actions/archive.bzl +++ b/go/private/actions/archive.bzl @@ -190,6 +190,7 @@ def emit_archive(go, source = None): label = source.library.label, importpath = source.library.importpath, importmap = source.library.importmap, + importpath_aliases = source.library.importpath_aliases, pathtype = source.library.pathtype, file = out_lib, export_file = out_export, diff --git a/go/private/actions/compile.bzl b/go/private/actions/compile.bzl index bf61acedf1..fbad115513 100644 --- a/go/private/actions/compile.bzl +++ b/go/private/actions/compile.bzl @@ -18,8 +18,10 @@ load( ) def _archive(v): + importpaths = [v.data.importpath] + importpaths.extend(v.data.importpath_aliases) return "{}={}={}={}".format( - v.data.importpath, + ":".join(importpaths), v.data.importmap, v.data.file.path, v.data.export_file.path if v.data.export_file else "", diff --git a/go/private/actions/compilepkg.bzl b/go/private/actions/compilepkg.bzl index edb81fc440..97e47b054d 100644 --- a/go/private/actions/compilepkg.bzl +++ b/go/private/actions/compilepkg.bzl @@ -22,8 +22,10 @@ load( ) def _archive(v): + importpaths = [v.data.importpath] + importpaths.extend(v.data.importpath_aliases) return "{}={}={}={}".format( - v.data.importpath, + ":".join(importpaths), v.data.importmap, v.data.file.path, v.data.export_file.path if v.data.export_file else "", diff --git a/go/private/context.bzl b/go/private/context.bzl index e9ab9d56cc..6b74b08e89 100644 --- a/go/private/context.bzl +++ b/go/private/context.bzl @@ -158,6 +158,7 @@ def _new_library(go, name = None, importpath = None, resolver = None, importable label = go._ctx.label, importpath = importpath, importmap = importmap, + importpath_aliases = go.importpath_aliases, pathtype = pathtype, resolve = resolver, testfilter = testfilter, @@ -294,6 +295,20 @@ def _check_binary_dep(go, dep, edge): edge = edge, )) +def _check_importpaths(ctx): + paths = [] + p = getattr(ctx.attr, "importpath", "") + if p: + paths.append(p) + p = getattr(ctx.attr, "importmap", "") + if p: + paths.append(p) + paths.extend(getattr(ctx.attr, "importpath_aliases", ())) + + for p in paths: + if ":" in p: + fail("import path '%s' contains invalid character :" % p) + def _infer_importpath(ctx): DEFAULT_LIB = "go_default_library" VENDOR_PREFIX = "/vendor/" @@ -380,7 +395,10 @@ def go_context(ctx, attr = None): toolchain.sdk.libs + toolchain.sdk.tools) + _check_importpaths(ctx) importpath, importmap, pathtype = _infer_importpath(ctx) + importpath_aliases = tuple(getattr(attr, "importpath_aliases", ())) + return GoContext( # Fields toolchain = toolchain, @@ -399,6 +417,7 @@ def go_context(ctx, attr = None): package_list = toolchain.sdk.package_list, importpath = importpath, importmap = importmap, + importpath_aliases = importpath_aliases, pathtype = pathtype, cgo_tools = context_data.cgo_tools, nogo = nogo, diff --git a/go/private/rules/library.bzl b/go/private/rules/library.bzl index 85c6e9e5ab..42cf2e0b6d 100644 --- a/go/private/rules/library.bzl +++ b/go/private/rules/library.bzl @@ -64,6 +64,7 @@ go_library = go_rule( "deps": attr.label_list(providers = [GoLibrary]), "importpath": attr.string(), "importmap": attr.string(), + "importpath_aliases": attr.string_list(), # experimental, undocumented "embed": attr.label_list(providers = [GoLibrary]), "gc_goopts": attr.string_list(), "x_defs": attr.string_dict(), diff --git a/go/private/rules/nogo.bzl b/go/private/rules/nogo.bzl index 1db18c13a7..4fa062cb3a 100644 --- a/go/private/rules/nogo.bzl +++ b/go/private/rules/nogo.bzl @@ -64,6 +64,7 @@ def _nogo_impl(ctx): label = go._ctx.label, importpath = "nogomain", importmap = "nogomain", + importpath_aliases = (), pathtype = EXPORT_PATH, resolve = None, ) diff --git a/go/private/rules/test.bzl b/go/private/rules/test.bzl index 891f01ab9f..be312e6b13 100644 --- a/go/private/rules/test.bzl +++ b/go/private/rules/test.bzl @@ -133,6 +133,7 @@ def _go_test_impl(ctx): label = go._ctx.label, importpath = "testmain", importmap = "testmain", + importpath_aliases = (), pathtype = INFERRED_PATH, resolve = None, ) diff --git a/go/tools/builders/compile.go b/go/tools/builders/compile.go index c26699514c..ecaf711b95 100644 --- a/go/tools/builders/compile.go +++ b/go/tools/builders/compile.go @@ -21,7 +21,6 @@ import ( "flag" "fmt" "io/ioutil" - "log" "os" "os/exec" "path/filepath" @@ -109,13 +108,13 @@ func compile(args []string) error { // Check that the filtered sources don't import anything outside of // the standard library and the direct dependencies. - _, stdImports, err := checkDirectDeps(goFiles, archives, *packageList) + imports, err := checkImports(goFiles, archives, *packageList) if err != nil { return err } // Build an importcfg file for the compiler. - importcfgName, err := buildImportcfgFileForCompile(archives, stdImports, goenv.installSuffix, filepath.Dir(*output)) + importcfgName, err := buildImportcfgFileForCompile(imports, goenv.installSuffix, filepath.Dir(*output)) if err != nil { return err } @@ -163,9 +162,6 @@ func compile(args []string) error { var nogoargs []string nogoargs = append(nogoargs, "-p", *packagePath) nogoargs = append(nogoargs, "-importcfg", importcfgName) - for _, imp := range stdImports { - nogoargs = append(nogoargs, "-stdimport", imp) - } for _, arc := range archives { if arc.xFile != "" { nogoargs = append(nogoargs, "-fact", fmt.Sprintf("%s=%s", arc.importPath, arc.xFile)) @@ -197,83 +193,3 @@ func compile(args []string) error { } return nil } - -func checkDirectDeps(files []fileInfo, archives []archive, packageList string) (depImports, stdImports []string, err error) { - packagesTxt, err := ioutil.ReadFile(packageList) - if err != nil { - log.Fatal(err) - } - stdlibSet := map[string]bool{} - for _, line := range strings.Split(string(packagesTxt), "\n") { - line = strings.TrimSpace(line) - if line != "" { - stdlibSet[line] = true - } - } - - depSet := map[string]bool{} - depList := make([]string, len(archives)) - for i, arc := range archives { - depSet[arc.importPath] = true - depList[i] = arc.importPath - } - - importSet := map[string]bool{} - - derr := depsError{known: depList} - for _, f := range files { - for _, path := range f.imports { - if path == "C" || isRelative(path) || importSet[path] { - // TODO(#1645): Support local (relative) import paths. We don't emit - // errors for them here, but they will probably break something else. - continue - } - if stdlibSet[path] { - stdImports = append(stdImports, path) - continue - } - if depSet[path] { - depImports = append(depImports, path) - continue - } - derr.missing = append(derr.missing, missingDep{f.filename, path}) - } - } - if len(derr.missing) > 0 { - return nil, nil, derr - } - return depImports, stdImports, nil -} - -type depsError struct { - missing []missingDep - known []string -} - -type missingDep struct { - filename, imp string -} - -var _ error = depsError{} - -func (e depsError) Error() string { - buf := bytes.NewBuffer(nil) - fmt.Fprintf(buf, "missing strict dependencies:\n") - for _, dep := range e.missing { - fmt.Fprintf(buf, "\t%s: import of %q\n", dep.filename, dep.imp) - } - if len(e.known) == 0 { - fmt.Fprintln(buf, "No dependencies were provided.") - } else { - fmt.Fprintln(buf, "Known dependencies are:") - for _, imp := range e.known { - fmt.Fprintf(buf, "\t%s\n", imp) - } - } - fmt.Fprint(buf, "Check that imports in Go sources match importpath attributes in deps.") - return buf.String() -} - -func isRelative(path string) bool { - return strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../") -} diff --git a/go/tools/builders/compilepkg.go b/go/tools/builders/compilepkg.go index e585df415f..96446fdcbe 100644 --- a/go/tools/builders/compilepkg.go +++ b/go/tools/builders/compilepkg.go @@ -266,27 +266,33 @@ func compileArchive( // Check that the filtered sources don't import anything outside of // the standard library and the direct dependencies. - _, stdImports, err := checkDirectDeps(srcs.goSrcs, deps, packageListPath) + imports, err := checkImports(srcs.goSrcs, deps, packageListPath) if err != nil { return err } if cgoEnabled && len(cgoSrcs) != 0 { // cgo generated code imports some extra packages. - cgoStdImports := map[string]struct{}{ - "runtime/cgo": struct{}{}, - "syscall": struct{}{}, - "unsafe": struct{}{}, - } - for _, imp := range stdImports { - delete(cgoStdImports, imp) + imports["runtime/cgo"] = nil + imports["syscall"] = nil + imports["unsafe"] = nil + } + if coverMode != "" { + const coverdataPath = "github.com/bazelbuild/rules_go/go/tools/coverdata" + var coverdata *archive + for i := range deps { + if deps[i].importPath == coverdataPath { + coverdata = &deps[i] + break + } } - for imp := range cgoStdImports { - stdImports = append(stdImports, imp) + if coverdata == nil { + return errors.New("coverage requested but coverdata dependency not provided") } + imports[coverdataPath] = coverdata } // Build an importcfg file for the compiler. - importcfgPath, err := buildImportcfgFileForCompile(deps, stdImports, goenv.installSuffix, filepath.Dir(outPath)) + importcfgPath, err := buildImportcfgFileForCompile(imports, goenv.installSuffix, filepath.Dir(outPath)) if err != nil { return err } @@ -298,7 +304,7 @@ func compileArchive( ctx, cancel := context.WithCancel(context.Background()) nogoChan = make(chan error) go func() { - nogoChan <- runNogo(ctx, nogoPath, goSrcs, deps, stdImports, packagePath, importcfgPath, outFactsPath) + nogoChan <- runNogo(ctx, nogoPath, goSrcs, deps, packagePath, importcfgPath, outFactsPath) }() defer func() { if nogoChan != nil { @@ -402,13 +408,10 @@ func compileGo(goenv *env, srcs []string, packagePath, importcfgPath, asmHdrPath return goenv.runCommand(args) } -func runNogo(ctx context.Context, nogoPath string, srcs []string, deps []archive, stdImports []string, packagePath, importcfgPath, outFactsPath string) error { +func runNogo(ctx context.Context, nogoPath string, srcs []string, deps []archive, packagePath, importcfgPath, outFactsPath string) error { args := []string{nogoPath} args = append(args, "-p", packagePath) args = append(args, "-importcfg", importcfgPath) - for _, imp := range stdImports { - args = append(args, "-stdimport", imp) - } for _, dep := range deps { if dep.xFile != "" { args = append(args, "-fact", fmt.Sprintf("%s=%s", dep.importPath, dep.xFile)) diff --git a/go/tools/builders/importcfg.go b/go/tools/builders/importcfg.go index b5b29d0926..3573f689b2 100644 --- a/go/tools/builders/importcfg.go +++ b/go/tools/builders/importcfg.go @@ -24,38 +24,112 @@ import ( "log" "os" "path/filepath" + "sort" "strings" ) type archive struct { label, importPath, packagePath, aFile, xFile string + importPathAliases []string +} + +// checkImports verifies that each import in files refers to a +// direct dependendency in archives or to a standard library package +// listed in the file at stdPackageListPath. checkImports returns +// a map from source import paths to elements of archives or to nil +// for standard library packages. +func checkImports(files []fileInfo, archives []archive, stdPackageListPath string) (map[string]*archive, error) { + // Read the standard package list. + packagesTxt, err := ioutil.ReadFile(stdPackageListPath) + if err != nil { + return nil, err + } + stdPkgs := make(map[string]bool) + for len(packagesTxt) > 0 { + n := bytes.IndexByte(packagesTxt, '\n') + var line string + if n < 0 { + line = string(packagesTxt) + packagesTxt = nil + } else { + line = string(packagesTxt[:n]) + packagesTxt = packagesTxt[n+1:] + } + line = strings.TrimSpace(line) + if line == "" { + continue + } + stdPkgs[line] = true + } + + // Index the archives. + importToArchive := make(map[string]*archive) + importAliasToArchive := make(map[string]*archive) + for i := range archives { + arc := &archives[i] + importToArchive[arc.importPath] = arc + for _, imp := range arc.importPathAliases { + importAliasToArchive[imp] = arc + } + } + + // Build the import map. + imports := make(map[string]*archive) + var derr depsError + for _, f := range files { + for _, path := range f.imports { + if _, ok := imports[path]; ok || path == "C" || isRelative(path) { + // TODO(#1645): Support local (relative) import paths. We don't emit + // errors for them here, but they will probably break something else. + continue + } + if stdPkgs[path] { + imports[path] = nil + } else if arc := importToArchive[path]; arc != nil { + imports[path] = arc + } else if arc := importAliasToArchive[path]; arc != nil { + imports[path] = arc + } else { + derr.missing = append(derr.missing, missingDep{f.filename, path}) + } + } + } + if len(derr.missing) > 0 { + return nil, derr + } + return imports, nil } // buildImportcfgFileForCompile writes an importcfg file to be consumed by the // compiler. The file is constructed from direct dependencies and std imports. // The caller is responsible for deleting the importcfg file. -func buildImportcfgFileForCompile(archives []archive, stdImports []string, installSuffix, dir string) (string, error) { +func buildImportcfgFileForCompile(imports map[string]*archive, installSuffix, dir string) (string, error) { buf := &bytes.Buffer{} goroot, ok := os.LookupEnv("GOROOT") if !ok { return "", errors.New("GOROOT not set") } goroot = abs(goroot) - // UGLY HACK: The vet tool called by compile program expects the vet.cfg file - // passed to it to contain import information for package fmt. Since we use - // importcfg to create vet.cfg, ensure that an entry for package fmt exists in - // the former. - stdImports = append(stdImports, "fmt") - for _, imp := range stdImports { - path := filepath.Join(goroot, "pkg", installSuffix, filepath.FromSlash(imp)) - fmt.Fprintf(buf, "packagefile %s=%s.a\n", imp, path) + + sortedImports := make([]string, 0, len(imports)) + for imp := range imports { + sortedImports = append(sortedImports, imp) } - for _, arc := range archives { - if arc.importPath != arc.packagePath { - fmt.Fprintf(buf, "importmap %s=%s\n", arc.importPath, arc.packagePath) + sort.Strings(sortedImports) + + for _, imp := range sortedImports { + if arc := imports[imp]; arc == nil { + // std package + path := filepath.Join(goroot, "pkg", installSuffix, filepath.FromSlash(imp)) + fmt.Fprintf(buf, "packagefile %s=%s.a\n", imp, path) + } else { + if imp != arc.packagePath { + fmt.Fprintf(buf, "importmap %s=%s\n", imp, arc.packagePath) + } + fmt.Fprintf(buf, "packagefile %s=%s\n", arc.packagePath, arc.aFile) } - fmt.Fprintf(buf, "packagefile %s=%s\n", arc.packagePath, arc.aFile) } + f, err := ioutil.TempFile(dir, "importcfg") if err != nil { return "", err @@ -73,19 +147,19 @@ func buildImportcfgFileForCompile(archives []archive, stdImports []string, insta return filename, nil } -func buildImportcfgFileForLink(archives []archive, packageList, installSuffix, dir string) (string, error) { +func buildImportcfgFileForLink(archives []archive, stdPackageListPath, installSuffix, dir string) (string, error) { buf := &bytes.Buffer{} goroot, ok := os.LookupEnv("GOROOT") if !ok { return "", errors.New("GOROOT not set") } prefix := abs(filepath.Join(goroot, "pkg", installSuffix)) - packageListFile, err := os.Open(packageList) + stdPackageListFile, err := os.Open(stdPackageListPath) if err != nil { return "", err } - defer packageListFile.Close() - scanner := bufio.NewScanner(packageListFile) + defer stdPackageListFile.Close() + scanner := bufio.NewScanner(stdPackageListFile) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" { @@ -128,6 +202,39 @@ This will be an error in the future.`, arc.packagePath, arc.label, conflictLabel return filename, nil } +type depsError struct { + missing []missingDep + known []string +} + +type missingDep struct { + filename, imp string +} + +var _ error = depsError{} + +func (e depsError) Error() string { + buf := bytes.NewBuffer(nil) + fmt.Fprintf(buf, "missing strict dependencies:\n") + for _, dep := range e.missing { + fmt.Fprintf(buf, "\t%s: import of %q\n", dep.filename, dep.imp) + } + if len(e.known) == 0 { + fmt.Fprintln(buf, "No dependencies were provided.") + } else { + fmt.Fprintln(buf, "Known dependencies are:") + for _, imp := range e.known { + fmt.Fprintf(buf, "\t%s\n", imp) + } + } + fmt.Fprint(buf, "Check that imports in Go sources match importpath attributes in deps.") + return buf.String() +} + +func isRelative(path string) bool { + return strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../") +} + // TODO(jayconrod): consolidate compile and link archive flags. type compileArchiveMultiFlag []archive @@ -144,10 +251,12 @@ func (m *compileArchiveMultiFlag) Set(v string) error { if len(parts) != 4 { return fmt.Errorf("badly formed -arc flag: %s", v) } + importPaths := strings.Split(parts[0], ":") a := archive{ - importPath: parts[0], - packagePath: parts[1], - aFile: abs(parts[2]), + importPath: importPaths[0], + importPathAliases: importPaths[1:], + packagePath: parts[1], + aFile: abs(parts[2]), } if parts[3] != "" { a.xFile = abs(parts[3]) diff --git a/go/tools/builders/nogo_main.go b/go/tools/builders/nogo_main.go index dee0f34299..30306127fa 100644 --- a/go/tools/builders/nogo_main.go +++ b/go/tools/builders/nogo_main.go @@ -62,10 +62,8 @@ func main() { // run returns an error if there is a problem loading the package or if any // analysis fails. func run(args []string) error { - stdImports := multiFlag{} factMap := factMultiFlag{} flags := flag.NewFlagSet("nogo", flag.ExitOnError) - flags.Var(&stdImports, "stdimport", "A standard library import path") flags.Var(&factMap, "fact", "Import path and file containing facts for that library, separated by '=' (may be repeated)'") importcfg := flags.String("importcfg", "", "The import configuration file") packagePath := flags.String("p", "", "The package path (importmap) of the package being compiled") @@ -77,12 +75,8 @@ func run(args []string) error { if err != nil { return fmt.Errorf("error parsing importcfg: %v", err) } - stdImportSet := make(map[string]bool) - for _, i := range stdImports { - stdImportSet[i] = true - } - diagnostics, facts, err := checkPackage(analyzers, *packagePath, packageFile, importMap, stdImportSet, factMap, srcs) + diagnostics, facts, err := checkPackage(analyzers, *packagePath, packageFile, importMap, factMap, srcs) if err != nil { return fmt.Errorf("error running analyzers: %v", err) } @@ -146,7 +140,7 @@ func readImportCfg(file string) (packageFile map[string]string, importMap map[st // It returns an empty string if no source code diagnostics need to be printed. // // This implementation was adapted from that of golang.org/x/tools/go/checker/internal/checker. -func checkPackage(analyzers []*analysis.Analyzer, packagePath string, packageFile, importMap map[string]string, stdImports map[string]bool, factMap map[string]string, filenames []string) (string, []byte, error) { +func checkPackage(analyzers []*analysis.Analyzer, packagePath string, packageFile, importMap map[string]string, factMap map[string]string, filenames []string) (string, []byte, error) { // Register fact types and establish dependencies between analyzers. actions := make(map[*analysis.Analyzer]*action) var visit func(a *analysis.Analyzer) *action @@ -177,7 +171,7 @@ func checkPackage(analyzers []*analysis.Analyzer, packagePath string, packageFil } // Load the package, including AST, types, and facts. - imp := newImporter(importMap, packageFile, stdImports, factMap) + imp := newImporter(importMap, packageFile, factMap) pkg, err := load(packagePath, imp, filenames) if err != nil { return "", nil, fmt.Errorf("error loading package: %v", err) @@ -449,17 +443,15 @@ type importer struct { importMap map[string]string // map import path in source code to package path packageCache map[string]*types.Package // cache of previously imported packages packageFile map[string]string // map package path to .a file with export data - stdImports map[string]bool // imports from the standard library factMap map[string]string // map import path in source code to file containing serialized facts } -func newImporter(importMap, packageFile map[string]string, stdImports map[string]bool, factMap map[string]string) *importer { +func newImporter(importMap, packageFile map[string]string, factMap map[string]string) *importer { return &importer{ fset: token.NewFileSet(), importMap: importMap, packageCache: make(map[string]*types.Package), packageFile: packageFile, - stdImports: stdImports, factMap: factMap, } } diff --git a/go/tools/builders/nogo_vet.go b/go/tools/builders/nogo_vet.go deleted file mode 100644 index 1b6fb4ffbc..0000000000 --- a/go/tools/builders/nogo_vet.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2018 The Bazel Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "os" - "os/exec" - "regexp" - "strings" -) - -// vet runs the 'go vet tool' command with the given vet configuration file and -// returns the emitted findings. It returns an error only if vet emits no -// findings and at least one error message. -func runVet(vetTool, vcfg string) (string, error) { - args := append(vetFlags, vcfg) - cmd := exec.Command(vetTool, args...) - out, err := cmd.CombinedOutput() - // Note: 'go tool vet' emits a non-zero return code both when vet encounters - // an internal error and when vet finds legitimate issues. - if err == nil { - return "", nil - } - vetFindings, vetErrors := []string{}, []string{} - errMsgs := splitOutput(string(out)) - for _, m := range errMsgs { - if !vetErrorMsgPattern.MatchString(m) { - vetErrors = append(vetErrors, m) - continue - } - vetFindings = append(vetFindings, m) - } - if len(vetFindings) == 0 && len(vetErrors) != 0 { - return "", errors.New(strings.Join(vetErrors, "\n")) - } - return strings.Join(vetFindings, "\n"), nil -} - -var vetFlags = []string{ - // NOTE: Keep in sync with github.com/golang/go/src/cmd/go/internal/test/test.go - "-atomic", - "-bool", - "-buildtags", - "-nilfunc", - "-printf", -} - -// vetErrorMsgPattern matches an error message emitted by vet. -// -// This regexp should be strict enough to exclude internal errors (prefixed with -// "vet") and type-check errors (which additionally include a column number). -// -// NOTE: this might break if the formating of vet error messages changes. -var vetErrorMsgPattern = regexp.MustCompile(`^([^:]+?):([0-9]+): (.*)`) - -// splitOutput was adapted from go/test/run.go. -func splitOutput(out string) []string { - // gc error messages continue onto additional lines with leading tabs. - // Split the output at the beginning of each line that doesn't begin with a tab. - // lines are impossible to match so those are filtered out. - var res []string - for _, line := range strings.Split(out, "\n") { - line = strings.TrimSuffix(line, "\r") // normalize Windows output - if strings.HasPrefix(line, "\t") { - res[len(res)-1] += "\n" + line - } else if strings.HasPrefix(line, "go tool") || strings.HasPrefix(line, "#") { - continue - } else if strings.TrimSpace(line) != "" { - res = append(res, line) - } - } - return res -} - -// buildVetcfgFile creates a vet.cfg file and returns its file path. It is the -// caller's responsibility to remove this file when it is no longer needed. -func buildVetcfgFile(packageFile, importMap map[string]string, stdImports, files []string) (vcfgPath_ string, err error) { - for path := range packageFile { - if _, ok := importMap[path]; !ok { - // vet expects every import path to be in the import map, even if the - // mapping is redundant. - importMap[path] = path - } - } - vcfg := &vetConfig{ - Compiler: "gc", // gccgo is currently not supported - GoFiles: files, - ImportMap: importMap, - PackageFile: packageFile, - Standard: make(map[string]bool), - SucceedOnTypecheckFailure: false, - } - for _, imp := range stdImports { - vcfg.Standard[imp] = true - } - - // GRIPE: vet checks whether its first positional argument has the suffix - // "vet.cfg", rather than accepting the file as an option. - vcfgFile, err := ioutil.TempFile("", "nogo-*-vet.cfg") - if err != nil { - return "", err - } - vcfgPath := vcfgFile.Name() // vcfgPath may be "" when returning - defer func() { - if cerr := vcfgFile.Close(); err == nil && cerr != nil { - err = cerr - } - if err != nil { - os.Remove(vcfgPath) - } - }() - - enc := json.NewEncoder(vcfgFile) - enc.SetIndent("", "\t") - if err := enc.Encode(vcfg); err != nil { - return "", fmt.Errorf("internal error marshaling vet config: %v", err) - } - - return vcfgPath, nil -} - -// vetConfig is the configuration passed to vet describing a single package. -// NOTE: Keep in sync with github.com/golang/go/internal/work/exec.go -type vetConfig struct { - Compiler string // compiler name (gc, gccgo) - Dir string // directory containing package - ImportPath string // canonical import path ("package path") - GoFiles []string // absolute paths to package source files - - ImportMap map[string]string // map import path in source code to package path - PackageFile map[string]string // map package path to .a file with export data - Standard map[string]bool // map package path to whether it's in the standard library - PackageVetx map[string]string // map package path to vetx data from earlier vet run - VetxOnly bool // only compute vetx data; don't report detected problems - VetxOutput string // write vetx data to this output file - - SucceedOnTypecheckFailure bool // awful hack; see #18395 and below -} diff --git a/tests/core/go_library/BUILD.bazel b/tests/core/go_library/BUILD.bazel index 72a1d16cbd..9e0737acaf 100644 --- a/tests/core/go_library/BUILD.bazel +++ b/tests/core/go_library/BUILD.bazel @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "empty", @@ -66,3 +66,32 @@ go_library( srcs = ["package_height_dep_shallow.go"], importpath = "package_height/dep", ) + +go_test( + name = "import_alias_test", + srcs = ["import_alias_test.go"], + deps = [ + ":import_alias_a_v2", + ":import_alias_b", + ":import_alias_b_v2", + ], +) + +go_library( + name = "import_alias_a_v2", + srcs = ["import_alias_a_v2.go"], + importpath = "import_alias/a/v2", + importpath_aliases = ["import_alias/a"], +) + +go_library( + name = "import_alias_b", + srcs = ["import_alias_b.go"], + importpath = "import_alias/b", +) + +go_library( + name = "import_alias_b_v2", + importpath = "import_alias/b/v2", + importpath_aliases = ["import_alias/b"], +) diff --git a/tests/core/go_library/README.rst b/tests/core/go_library/README.rst index 4e90c4a99f..415a19e4aa 100644 --- a/tests/core/go_library/README.rst +++ b/tests/core/go_library/README.rst @@ -5,6 +5,7 @@ Basic go_library functionality .. #1262: https://github.com/bazelbuild/rules_go/issues/1262 .. #1520: https://github.com/bazelbuild/rules_go/issues/1520 .. #1772: https://github.com/bazelbuild/rules_go/issues/1772 +.. #2058: https://github.com/bazelbuild/rules_go/issues/2058 empty ----- @@ -29,3 +30,10 @@ package_height Checks that when a library embeds another library, the embedder's dependencies may override the embeddee's dependencies. Verifies `#1772`_. + +import_alias_test +----------------- + +Checks that a library may import another library using one of the strings +listed in ``importpath_aliases``. This is the basic mechanism for minimal +module compatibility. Verifies `#2058`_. diff --git a/tests/core/go_library/import_alias_a_v2.go b/tests/core/go_library/import_alias_a_v2.go new file mode 100644 index 0000000000..66326f9475 --- /dev/null +++ b/tests/core/go_library/import_alias_a_v2.go @@ -0,0 +1,17 @@ +// Copyright 2019 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package a + +const A = "import_alias/a/v2" diff --git a/tests/core/go_library/import_alias_b.go b/tests/core/go_library/import_alias_b.go new file mode 100644 index 0000000000..1fa799779a --- /dev/null +++ b/tests/core/go_library/import_alias_b.go @@ -0,0 +1,17 @@ +// Copyright 2019 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package b + +const B = "import_alias/b" diff --git a/tests/core/go_library/import_alias_test.go b/tests/core/go_library/import_alias_test.go new file mode 100644 index 0000000000..aabec973e1 --- /dev/null +++ b/tests/core/go_library/import_alias_test.go @@ -0,0 +1,33 @@ +// Copyright 2019 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package import_alias + +import ( + "import_alias/a" + "import_alias/b" + "testing" +) + +func TestA(t *testing.T) { + if a.A != "import_alias/a/v2" { + t.Errorf("got %q; want %q", a.A, "import_alias/a/v2") + } +} + +func TestB(t *testing.T) { + if b.B != "import_alias/b" { + t.Errorf("got %q; want %q", b.B, "import_alias/b") + } +}