• 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	"--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