• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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