github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/testutils/buildutil/build.go (about)

     1  // Copyright 2015 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package buildutil
    12  
    13  import (
    14  	"go/build"
    15  	"os"
    16  	"runtime"
    17  	"strings"
    18  	"testing"
    19  
    20  	"github.com/cockroachdb/errors"
    21  )
    22  
    23  func init() {
    24  	// NB: This is a hack to disable the use of go modules with
    25  	// build.Import. This will probably break with a future version of Go, but
    26  	// suffices until we move to using go modules. See go/build.Context.importGo
    27  	// (https://github.com/golang/go/blob/master/src/go/build/build.go#L999) and
    28  	// the logic to skip using `go list` if the env far "GO111MODULE" is set to
    29  	// "off".
    30  	_ = os.Setenv("GO111MODULE", "off")
    31  }
    32  
    33  func short(in string) string {
    34  	return strings.Replace(in, "github.com/cockroachdb/cockroach/pkg/", "./pkg/", -1)
    35  }
    36  
    37  // VerifyNoImports verifies that a package doesn't depend (directly or
    38  // indirectly) on forbidden packages. The forbidden packages are specified as
    39  // either exact matches or prefix matches.
    40  // A match is not reported if the package that includes the forbidden package
    41  // is listed in the whitelist.
    42  // If GOPATH isn't set, it is an indication that the source is not available and
    43  // the test is skipped.
    44  func VerifyNoImports(
    45  	t testing.TB,
    46  	pkgPath string,
    47  	cgo bool,
    48  	forbiddenPkgs, forbiddenPrefixes []string,
    49  	whitelist ...string,
    50  ) {
    51  
    52  	// Skip test if source is not available.
    53  	if build.Default.GOPATH == "" {
    54  		t.Skip("GOPATH isn't set")
    55  	}
    56  
    57  	buildContext := build.Default
    58  	buildContext.CgoEnabled = cgo
    59  
    60  	checked := make(map[string]struct{})
    61  
    62  	var check func(string) error
    63  	check = func(path string) error {
    64  		pkg, err := buildContext.Import(path, "", 0)
    65  		if err != nil {
    66  			t.Fatal(err)
    67  		}
    68  		for _, imp := range pkg.Imports {
    69  			for _, forbidden := range forbiddenPkgs {
    70  				if forbidden == imp {
    71  					whitelisted := false
    72  					for _, w := range whitelist {
    73  						if path == w {
    74  							whitelisted = true
    75  							break
    76  						}
    77  					}
    78  					if !whitelisted {
    79  						return errors.Errorf("%s imports %s, which is forbidden", short(path), short(imp))
    80  					}
    81  				}
    82  				if forbidden == "c-deps" &&
    83  					imp == "C" &&
    84  					strings.HasPrefix(path, "github.com/cockroachdb/cockroach/pkg") &&
    85  					path != "github.com/cockroachdb/cockroach/pkg/geo/geoproj" {
    86  					for _, name := range pkg.CgoFiles {
    87  						if strings.Contains(name, "zcgo_flags") {
    88  							return errors.Errorf("%s imports %s (%s), which is forbidden", short(path), short(imp), name)
    89  						}
    90  					}
    91  				}
    92  			}
    93  			for _, prefix := range forbiddenPrefixes {
    94  				if strings.HasPrefix(imp, prefix) {
    95  					return errors.Errorf("%s imports %s which has prefix %s, which is forbidden", short(path), short(imp), prefix)
    96  				}
    97  			}
    98  
    99  			// https://github.com/golang/tools/blob/master/refactor/importgraph/graph.go#L159
   100  			if imp == "C" {
   101  				continue // "C" is fake
   102  			}
   103  
   104  			importPkg, err := buildContext.Import(imp, pkg.Dir, build.FindOnly)
   105  			if err != nil {
   106  				// go/build does not know that gccgo's standard packages don't have
   107  				// source, and will report an error saying that it can not find them.
   108  				//
   109  				// See https://github.com/golang/go/issues/16701
   110  				// and https://github.com/golang/go/issues/23607.
   111  				if runtime.Compiler == "gccgo" {
   112  					continue
   113  				}
   114  				t.Fatal(err)
   115  			}
   116  			imp = importPkg.ImportPath
   117  			if _, ok := checked[imp]; ok {
   118  				continue
   119  			}
   120  			if err := check(imp); err != nil {
   121  				return errors.Wrapf(err, "%s depends on", short(path))
   122  			}
   123  			checked[pkg.ImportPath] = struct{}{}
   124  		}
   125  		return nil
   126  	}
   127  	if err := check(pkgPath); err != nil {
   128  		t.Fatal(err)
   129  	}
   130  }
   131  
   132  // VerifyTransitiveWhitelist checks that the entire set of transitive
   133  // dependencies of the given package is in a whitelist. Vendored and stdlib
   134  // packages are always allowed.
   135  func VerifyTransitiveWhitelist(t testing.TB, pkg string, allowedPkgs []string) {
   136  	// Skip test if source is not available.
   137  	if build.Default.GOPATH == "" {
   138  		t.Skip("GOPATH isn't set")
   139  	}
   140  
   141  	checked := make(map[string]struct{})
   142  	allowed := make(map[string]struct{}, len(allowedPkgs))
   143  	for _, allowedPkg := range allowedPkgs {
   144  		allowed[allowedPkg] = struct{}{}
   145  	}
   146  
   147  	var check func(string)
   148  	check = func(path string) {
   149  		if _, ok := checked[path]; ok {
   150  			return
   151  		}
   152  		checked[path] = struct{}{}
   153  		if strings.HasPrefix(path, "github.com/cockroachdb/cockroach/vendor") {
   154  			return
   155  		}
   156  
   157  		pkg, err := build.Default.Import(path, "", 0)
   158  		if err != nil {
   159  			t.Fatal(err)
   160  		}
   161  		for _, imp := range pkg.Imports {
   162  			if !strings.HasPrefix(imp, "github.com/cockroachdb/cockroach/") {
   163  				continue
   164  			}
   165  			if _, ok := allowed[imp]; !ok {
   166  				t.Errorf("%s imports %s, which is forbidden", short(path), short(imp))
   167  				// If we can't have this package, don't bother recursively checking the
   168  				// deps, they'll just be noise.
   169  				continue
   170  			}
   171  
   172  			// https://github.com/golang/tools/blob/master/refactor/importgraph/graph.go#L159
   173  			if imp == "C" {
   174  				continue // "C" is fake
   175  			}
   176  
   177  			importPkg, err := build.Default.Import(imp, pkg.Dir, build.FindOnly)
   178  			if err != nil {
   179  				t.Fatal(err)
   180  			}
   181  			check(importPkg.ImportPath)
   182  		}
   183  	}
   184  	check(pkg)
   185  }