1// Copyright 2022 Google LLC 2// 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5 6package exporter 7 8import ( 9 "fmt" 10 "os" 11 "os/exec" 12 13 "go.skia.org/infra/go/skerr" 14 "go.skia.org/skia/bazel/exporter/interfaces" 15) 16 17// BazelQueryCommand implements the QueryCommand interface. It will 18// execute the bazel executable to return all rules defined in 19// a protocol buffer. 20type BazelQueryCommand struct { 21 ruleNames []string 22 workspace string 23 queryType string // "query" or "cquery" 24} 25 26// A list of all Skia Bazel build flags which enable the building and/or 27// exposure of all source files. 28var allSkiaFlags = []string{ 29 "--ck_enable_canvas_polyfill", 30 "--ck_enable_embedded_font", 31 "--ck_enable_fonts", 32 "--ck_enable_matrix_js", 33 "--ck_enable_runtime_effect", 34 "--ck_enable_skottie", 35 "--ck_enable_skp_serialization", 36} 37 38// NewBazelQueryCommand will create a new BazelQueryCommand instance which will, 39// when Read() is called, invoke the bazel executable to execute a cquery 40// command in the provided workspace for the supplied rules. 41func NewBazelCMakeQueryCommand(ruleNames []string, workspace string) *BazelQueryCommand { 42 return &BazelQueryCommand{ruleNames: ruleNames, workspace: workspace, queryType: "cquery"} 43} 44 45// NewBazelQueryCommand will create a new BazelQueryCommand instance which will, 46// when Read() is called, invoke the bazel executable to execute a query 47// command in the provided workspace for the supplied rules. 48func NewBazelGNIQueryCommand(ruleNames []string, workspace string) *BazelQueryCommand { 49 return &BazelQueryCommand{ruleNames: ruleNames, workspace: workspace, queryType: "query"} 50} 51 52// Stop the Bazel server if running. 53func shutdownBazelServer() error { 54 cmd := exec.Command("bazelisk", "shutdown") 55 _, err := cmd.Output() 56 return err 57} 58 59// Read will execute the Bazel query/cquery command, supplied to NewBazel*QueryCommand(), 60// and return the binary protobuf results. 61func (c *BazelQueryCommand) Read() ([]byte, error) { 62 if len(c.ruleNames) == 0 { 63 return nil, skerr.Fmt("no query rules") 64 } 65 66 pwd, err := os.Getwd() 67 if err != nil { 68 return nil, skerr.Wrapf(err, `can't get working directory`) 69 } 70 err = os.Chdir(c.workspace) 71 if err != nil { 72 return nil, skerr.Wrapf(err, `can't set working directory to %q`, c.workspace) 73 } 74 if c.queryType == "cquery" { 75 // Shutdown the Bazel server to workaround a known issue with cquery: 76 // See "Non-deterministic output" in https://bazel.build/docs/cquery#known-issues 77 err = shutdownBazelServer() 78 if err != nil { 79 return nil, skerr.Wrap(err) 80 } 81 } 82 ruleArg := `kind("rule", ` 83 for i, r := range c.ruleNames { 84 if i > 0 { 85 ruleArg = ruleArg + " + " 86 } 87 ruleArg = ruleArg + fmt.Sprintf("deps(%s)", r) 88 } 89 ruleArg = ruleArg + ")" 90 args := []string{c.queryType, "--noimplicit_deps", ruleArg, "--output", "proto"} 91 if c.queryType == "cquery" { 92 args = append(args, allSkiaFlags...) 93 } 94 cmd := exec.Command("bazelisk", args...) 95 _ = os.Chdir(pwd) 96 data, err := cmd.Output() 97 if err != nil { 98 if exiterr, ok := err.(*exec.ExitError); ok { 99 fmt.Printf("Stderr: %s\n", exiterr.Stderr) 100 } 101 return nil, skerr.Wrapf(err, `error running %v`, cmd) 102 } 103 return data, nil 104} 105 106// Make sure BazelQueryCommand fulfills the QueryCommand interface. 107var _ interfaces.QueryCommand = (*BazelQueryCommand)(nil) 108