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