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