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 }