• 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//
6// This executable runs a Bazel(isk) build command for a single label using the provided
7// config (which is assumed to be in //bazel/buildrc) and any provided Bazel args.
8// This handles any setup needed to run Bazel on our CI machines before running the task, like
9// setting up logs and the Bazel cache.
10package main
11
12import (
13	"context"
14	"flag"
15	"fmt"
16	"path/filepath"
17
18	"go.skia.org/infra/go/common"
19	sk_exec "go.skia.org/infra/go/exec"
20	"go.skia.org/infra/task_driver/go/lib/bazel"
21	"go.skia.org/infra/task_driver/go/lib/os_steps"
22	"go.skia.org/infra/task_driver/go/td"
23)
24
25var (
26	// Required properties for this task.
27	bazelArgs    = common.NewMultiStringFlag("bazel_arg", nil, "Additional arguments that should be forwarded directly to the Bazel invocation.")
28	cross        = flag.String("cross", "", "An identifier specifying the target platform that Bazel should build for. If empty, Bazel builds for the host platform (the machine on which this executable is run).")
29	config       = flag.String("config", "", "A custom configuration specified in //bazel/buildrc. This configuration potentially encapsulates many features and options.")
30	expungeCache = flag.Bool("expunge_cache", false, "If set, the Bazel cache will be cleaned with --expunge before execution. We should only have to set this rarely, if something gets messed up.")
31	projectId    = flag.String("project_id", "", "ID of the Google Cloud project.")
32	label        = flag.String("label", "", "An absolute label to the target that should be built.")
33	taskId       = flag.String("task_id", "", "ID of this task.")
34	taskName     = flag.String("task_name", "", "Name of the task.")
35	workdir      = flag.String("workdir", ".", "Working directory, the root directory of a full Skia checkout")
36	// Optional flags.
37	local  = flag.Bool("local", false, "True if running locally (as opposed to on the CI/CQ)")
38	output = flag.String("o", "", "If provided, dump a JSON blob of step data to the given file. Prints to stdout if '-' is given.")
39)
40
41func main() {
42	// StartRun calls flag.Parse()
43	ctx := td.StartRun(projectId, taskId, taskName, output, local)
44	defer td.EndRun(ctx)
45
46	if *label == "" || *config == "" {
47		td.Fatal(ctx, fmt.Errorf("--label and --config are required"))
48	}
49
50	wd, err := os_steps.Abs(ctx, *workdir)
51	if err != nil {
52		td.Fatal(ctx, err)
53	}
54	skiaDir := filepath.Join(wd, "skia")
55
56	opts := bazel.BazelOptions{
57		// We want the cache to be on a bigger disk than default. The root disk, where the home
58		// directory (and default Bazel cache) lives, is only 15 GB on our GCE VMs.
59		CachePath: "/mnt/pd0/bazel_cache",
60	}
61	if err := bazel.EnsureBazelRCFile(ctx, opts); err != nil {
62		td.Fatal(ctx, err)
63	}
64
65	if *cross != "" {
66		// See https://bazel.build/concepts/platforms-intro and https://bazel.build/docs/platforms
67		// when ready to support this.
68		td.Fatal(ctx, fmt.Errorf("cross compilation not yet supported"))
69	}
70
71	if *expungeCache {
72		if err := bazelClean(ctx, skiaDir); err != nil {
73			td.Fatal(ctx, err)
74		}
75	}
76
77	if err := bazelBuild(ctx, skiaDir, *label, *config, *bazelArgs...); err != nil {
78		td.Fatal(ctx, err)
79	}
80}
81
82// bazelBuild builds the target referenced by the given absolute label passing the provided
83// config and any additional args to the build command. Instead of calling Bazel directly, we use
84// Bazelisk to make sure we use the right version of Bazel, as defined in the .bazelversion file
85// at the Skia root.
86func bazelBuild(ctx context.Context, checkoutDir, label, config string, args ...string) error {
87	step := fmt.Sprintf("Build %s with config %s and %d extra flags", label, config, len(args))
88	return td.Do(ctx, td.Props(step), func(ctx context.Context) error {
89		runCmd := &sk_exec.Command{
90			Name: "bazelisk",
91			Args: append([]string{"build",
92				label,
93				"--config=" + config, // Should be defined in //bazel/buildrc
94			}, args...),
95			InheritEnv: true, // Makes sure bazelisk is on PATH
96			Dir:        checkoutDir,
97			LogStdout:  true,
98			LogStderr:  true,
99		}
100		_, err := sk_exec.RunCommand(ctx, runCmd)
101		if err != nil {
102			return err
103		}
104		return nil
105	})
106}
107
108// bazelClean cleans the bazel cache and the external directory via the --expunge flag.
109func bazelClean(ctx context.Context, checkoutDir string) error {
110	return td.Do(ctx, td.Props("Cleaning cache with --expunge"), func(ctx context.Context) error {
111		runCmd := &sk_exec.Command{
112			Name:       "bazelisk",
113			Args:       append([]string{"clean", "--expunge"}),
114			InheritEnv: true, // Makes sure bazelisk is on PATH
115			Dir:        checkoutDir,
116			LogStdout:  true,
117			LogStderr:  true,
118		}
119		_, err := sk_exec.RunCommand(ctx, runCmd)
120		if err != nil {
121			return err
122		}
123		return nil
124	})
125}
126