github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/build/bazel/dependencies.go (about) 1 /* 2 Copyright 2019 The Skaffold Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package bazel 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "os" 24 "os/exec" 25 "path/filepath" 26 "strings" 27 "sync" 28 "time" 29 30 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log" 31 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" 32 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" 33 ) 34 35 const sourceQuery = "kind('source file', deps('%[1]s')) union buildfiles(deps('%[1]s'))" 36 37 func query(target string) string { 38 return fmt.Sprintf(sourceQuery, target) 39 } 40 41 var once sync.Once 42 43 // GetDependencies finds the sources dependencies for the given bazel artifact. 44 // All paths are relative to the workspace. 45 func GetDependencies(ctx context.Context, dir string, a *latest.BazelArtifact) ([]string, error) { 46 timer := time.NewTimer(1 * time.Second) 47 defer timer.Stop() 48 49 go func() { 50 <-timer.C 51 once.Do(func() { log.Entry(ctx).Warn("Retrieving Bazel dependencies can take a long time the first time") }) 52 }() 53 54 topLevelFolder, err := findWorkspace(dir) 55 if err != nil { 56 return nil, fmt.Errorf("unable to find the WORKSPACE file: %w", err) 57 } 58 59 absDir, err := filepath.Abs(dir) 60 if err != nil { 61 return nil, fmt.Errorf("unable to find absolute path for %q: %w", dir, err) 62 } 63 64 cmd := exec.CommandContext(ctx, "bazel", "query", query(a.BuildTarget), "--noimplicit_deps", "--order_output=no", "--output=label") 65 cmd.Dir = dir 66 stdout, err := util.RunCmdOut(ctx, cmd) 67 if err != nil { 68 return nil, fmt.Errorf("getting bazel dependencies: %w", err) 69 } 70 71 labels := strings.Split(string(stdout), "\n") 72 var deps []string 73 for _, l := range labels { 74 if strings.HasPrefix(l, "@") { 75 continue 76 } 77 if strings.HasPrefix(l, "//external") { 78 continue 79 } 80 if l == "" { 81 continue 82 } 83 84 rel, err := filepath.Rel(absDir, filepath.Join(topLevelFolder, depToPath(l))) 85 if err != nil { 86 return nil, fmt.Errorf("unable to find absolute path: %w", err) 87 } 88 deps = append(deps, rel) 89 } 90 91 rel, err := filepath.Rel(absDir, filepath.Join(topLevelFolder, "WORKSPACE")) 92 if err != nil { 93 return nil, fmt.Errorf("unable to find absolute path: %w", err) 94 } 95 deps = append(deps, rel) 96 97 log.Entry(ctx).Debugf("Found dependencies for bazel artifact: %v", deps) 98 99 return deps, nil 100 } 101 102 func depToPath(dep string) string { 103 return strings.TrimPrefix(strings.Replace(strings.TrimPrefix(dep, "//"), ":", "/", 1), "/") 104 } 105 106 func findWorkspace(workingDir string) (string, error) { 107 dir, err := filepath.Abs(workingDir) 108 if err != nil { 109 return "", fmt.Errorf("invalid working dir: %w", err) 110 } 111 112 for { 113 if _, err := os.Stat(filepath.Join(dir, "WORKSPACE")); err == nil { 114 return dir, nil 115 } 116 117 parent := filepath.Dir(dir) 118 if parent == dir { 119 return "", errors.New("no WORKSPACE file found") 120 } 121 dir = parent 122 } 123 }