// Copyright 2022 Google LLC // // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package exporter import ( "fmt" "os" "os/exec" "go.skia.org/infra/go/skerr" "go.skia.org/skia/bazel/exporter/interfaces" ) // BazelQueryCommand implements the QueryCommand interface. It will // execute the bazel executable to return all rules defined in // a protocol buffer. type BazelQueryCommand struct { ruleNames []string workspace string queryType string // "query" or "cquery" } // A list of all Skia Bazel build flags which enable the building and/or // exposure of all source files. var allSkiaFlags = []string{ "--ck_enable_canvas_polyfill", "--ck_enable_embedded_font", "--ck_enable_fonts", "--ck_enable_matrix_js", "--ck_enable_runtime_effect", "--ck_enable_skottie", "--ck_enable_skp_serialization", "--enable_effect_serialization", "--enable_gpu_test_utils", "--enable_pdf_backend", // "--enable_skslc", // external dependency on spirv-tools/libspirv.hpp "--enable_svg_canvas", "--enable_tracing", "--enable_vma", "--gpu_backend=gl_backend", // "--include_decoder=*", // All decoders have external dependencies. // "--include_encoder", // All encoders have external dependencies. // "--include_fontmgr=custom_embedded_fontmgr", // external dependency on ft2build.h } // NewBazelQueryCommand will create a new BazelQueryCommand instance which will, // when Read() is called, invoke the bazel executable to execute a cquery // command in the provided workspace for the supplied rules. func NewBazelCMakeQueryCommand(ruleNames []string, workspace string) *BazelQueryCommand { return &BazelQueryCommand{ruleNames: ruleNames, workspace: workspace, queryType: "cquery"} } // NewBazelQueryCommand will create a new BazelQueryCommand instance which will, // when Read() is called, invoke the bazel executable to execute a query // command in the provided workspace for the supplied rules. func NewBazelGNIQueryCommand(ruleNames []string, workspace string) *BazelQueryCommand { return &BazelQueryCommand{ruleNames: ruleNames, workspace: workspace, queryType: "query"} } // Stop the Bazel server if running. func shutdownBazelServer() error { cmd := exec.Command("bazelisk", "shutdown") _, err := cmd.Output() return err } // Read will execute the Bazel query/cquery command, supplied to NewBazel*QueryCommand(), // and return the binary protobuf results. func (c *BazelQueryCommand) Read() ([]byte, error) { if len(c.ruleNames) == 0 { return nil, skerr.Fmt("no query rules") } pwd, err := os.Getwd() if err != nil { return nil, skerr.Wrapf(err, `can't get working directory`) } err = os.Chdir(c.workspace) if err != nil { return nil, skerr.Wrapf(err, `can't set working directory to %q`, c.workspace) } if c.queryType == "cquery" { // Shutdown the Bazel server to workaround a known issue with cquery: // See "Non-deterministic output" in https://bazel.build/docs/cquery#known-issues err = shutdownBazelServer() if err != nil { return nil, skerr.Wrap(err) } } ruleArg := `kind("rule", ` for i, r := range c.ruleNames { if i > 0 { ruleArg = ruleArg + " + " } ruleArg = ruleArg + fmt.Sprintf("deps(%s)", r) } ruleArg = ruleArg + ")" args := []string{c.queryType, "--noimplicit_deps", ruleArg, "--output", "proto"} if c.queryType == "cquery" { args = append(args, allSkiaFlags...) } cmd := exec.Command("bazelisk", args...) _ = os.Chdir(pwd) data, err := cmd.Output() if err != nil { if exiterr, ok := err.(*exec.ExitError); ok { fmt.Printf("Stderr: %s\n", exiterr.Stderr) } return nil, skerr.Wrapf(err, `error running %v`, cmd) } return data, nil } // Make sure BazelQueryCommand fulfills the QueryCommand interface. var _ interfaces.QueryCommand = (*BazelQueryCommand)(nil)