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 "--enable_effect_serialization", 37 "--enable_gpu_test_utils", 38 "--enable_pdf_backend", 39 // "--enable_skslc", // external dependency on spirv-tools/libspirv.hpp 40 "--enable_svg_canvas", 41 "--enable_tracing", 42 "--enable_vma", 43 "--gpu_backend=gl_backend", 44 // "--include_decoder=*", // All decoders have external dependencies. 45 // "--include_encoder", // All encoders have external dependencies. 46 // "--include_fontmgr=custom_embedded_fontmgr", // external dependency on ft2build.h 47} 48 49// NewBazelQueryCommand will create a new BazelQueryCommand instance which will, 50// when Read() is called, invoke the bazel executable to execute a cquery 51// command in the provided workspace for the supplied rules. 52func NewBazelCMakeQueryCommand(ruleNames []string, workspace string) *BazelQueryCommand { 53 return &BazelQueryCommand{ruleNames: ruleNames, workspace: workspace, queryType: "cquery"} 54} 55 56// NewBazelQueryCommand will create a new BazelQueryCommand instance which will, 57// when Read() is called, invoke the bazel executable to execute a query 58// command in the provided workspace for the supplied rules. 59func NewBazelGNIQueryCommand(ruleNames []string, workspace string) *BazelQueryCommand { 60 return &BazelQueryCommand{ruleNames: ruleNames, workspace: workspace, queryType: "query"} 61} 62 63// Stop the Bazel server if running. 64func shutdownBazelServer() error { 65 cmd := exec.Command("bazelisk", "shutdown") 66 _, err := cmd.Output() 67 return err 68} 69 70// Read will execute the Bazel query/cquery command, supplied to NewBazel*QueryCommand(), 71// and return the binary protobuf results. 72func (c *BazelQueryCommand) Read() ([]byte, error) { 73 if len(c.ruleNames) == 0 { 74 return nil, skerr.Fmt("no query rules") 75 } 76 77 pwd, err := os.Getwd() 78 if err != nil { 79 return nil, skerr.Wrapf(err, `can't get working directory`) 80 } 81 err = os.Chdir(c.workspace) 82 if err != nil { 83 return nil, skerr.Wrapf(err, `can't set working directory to %q`, c.workspace) 84 } 85 if c.queryType == "cquery" { 86 // Shutdown the Bazel server to workaround a known issue with cquery: 87 // See "Non-deterministic output" in https://bazel.build/docs/cquery#known-issues 88 err = shutdownBazelServer() 89 if err != nil { 90 return nil, skerr.Wrap(err) 91 } 92 } 93 ruleArg := `kind("rule", ` 94 for i, r := range c.ruleNames { 95 if i > 0 { 96 ruleArg = ruleArg + " + " 97 } 98 ruleArg = ruleArg + fmt.Sprintf("deps(%s)", r) 99 } 100 ruleArg = ruleArg + ")" 101 args := []string{c.queryType, "--noimplicit_deps", ruleArg, "--output", "proto"} 102 if c.queryType == "cquery" { 103 args = append(args, allSkiaFlags...) 104 } 105 cmd := exec.Command("bazelisk", args...) 106 _ = os.Chdir(pwd) 107 data, err := cmd.Output() 108 if err != nil { 109 if exiterr, ok := err.(*exec.ExitError); ok { 110 fmt.Printf("Stderr: %s\n", exiterr.Stderr) 111 } 112 return nil, skerr.Wrapf(err, `error running %v`, cmd) 113 } 114 return data, nil 115} 116 117// Make sure BazelQueryCommand fulfills the QueryCommand interface. 118var _ interfaces.QueryCommand = (*BazelQueryCommand)(nil) 119