| // Copyright 2022 Google LLC |
| // |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // This executable runs a Bazel(isk) build command for a single label using the provided |
| // config (which is assumed to be in //bazel/buildrc) and any provided Bazel args. |
| // This handles any setup needed to run Bazel on our CI machines before running the task, like |
| // setting up logs and the Bazel cache. |
| package main |
| |
| import ( |
| "context" |
| "flag" |
| "fmt" |
| "io/fs" |
| "path/filepath" |
| |
| infra_common "go.skia.org/infra/go/common" |
| "go.skia.org/infra/go/skerr" |
| "go.skia.org/infra/task_driver/go/lib/bazel" |
| "go.skia.org/infra/task_driver/go/lib/os_steps" |
| "go.skia.org/infra/task_driver/go/td" |
| "go.skia.org/skia/infra/bots/task_drivers/common" |
| ) |
| |
| var ( |
| // Required properties for this task. |
| projectId = flag.String("project_id", "", "ID of the Google Cloud project.") |
| taskId = flag.String("task_id", "", "ID of this task.") |
| taskName = flag.String("task_name", "", "Name of the task.") |
| workdir = flag.String("workdir", ".", "Working directory in which the build will be performed.") |
| outPath = flag.String("out_path", "", "Directory into which to copy the //bazel-bin subdirectories provided via --saved_output_dir. If unset, nothing will be copied.") |
| savedOutputDir = infra_common.NewMultiStringFlag("saved_output_dir", nil, `//bazel-bin subdirectories to copy into the path provided via --out_path (e.g. "tests" will copy the contents of //bazel-bin/tests).`) |
| |
| // Optional flags. |
| local = flag.Bool("local", false, "True if running locally (as opposed to on the CI/CQ)") |
| output = flag.String("o", "", "If provided, dump a JSON blob of step data to the given file. Prints to stdout if '-' is given.") |
| ) |
| |
| func main() { |
| bazelFlags := common.MakeBazelFlags(common.MakeBazelFlagsOpts{ |
| Label: true, |
| Config: true, |
| AdditionalArgs: true, |
| }) |
| |
| // StartRun calls flag.Parse() |
| ctx := td.StartRun(projectId, taskId, taskName, output, local) |
| defer td.EndRun(ctx) |
| |
| bazelFlags.Validate(ctx) |
| |
| if *outPath != "" && len(*savedOutputDir) == 0 { |
| td.Fatal(ctx, fmt.Errorf("at least one --saved_output_dir is required if --out_path is set")) |
| } |
| |
| checkoutPath, err := os_steps.Abs(ctx, *workdir) |
| if err != nil { |
| td.Fatal(ctx, err) |
| } |
| var outputPath string |
| if *outPath != "" { |
| outputPath, err = os_steps.Abs(ctx, *outPath) |
| if err != nil { |
| td.Fatal(ctx, err) |
| } |
| } |
| |
| opts := bazel.BazelOptions{ |
| // We want the cache to be on a bigger disk than default. The root disk, where the home |
| // directory (and default Bazel cache) lives, is only 15 GB on our GCE VMs. |
| CachePath: *bazelFlags.CacheDir, |
| } |
| bzl, err := bazel.New(ctx, checkoutPath, "", opts) |
| if err != nil { |
| td.Fatal(ctx, err) |
| } |
| |
| // Schedule the cleanup steps. |
| defer func() { |
| if !*local { |
| // Ignore any error here until after we've run "shutdown". |
| cleanErr := common.BazelCleanIfLowDiskSpace(ctx, *bazelFlags.CacheDir, checkoutPath, "bazelisk") |
| if _, err := bzl.Do(ctx, "shutdown"); err != nil { |
| td.Fatal(ctx, err) |
| } |
| if cleanErr != nil { |
| td.Fatal(ctx, cleanErr) |
| } |
| } |
| }() |
| |
| // Perform the build. |
| args := append([]string{*bazelFlags.Label, fmt.Sprintf("--config=%s", *bazelFlags.Config)}, *bazelFlags.AdditionalArgs...) |
| if _, err := bzl.Do(ctx, "build", args...); err != nil { |
| td.Fatal(ctx, err) |
| } |
| |
| if outputPath != "" { |
| if err := copyBazelBinSubdirs(ctx, checkoutPath, *savedOutputDir, outputPath); err != nil { |
| td.Fatal(ctx, err) |
| } |
| } |
| } |
| |
| // copyBazelBinSubdirs copies the contents of the bazel-bin directory into the given path. |
| func copyBazelBinSubdirs(ctx context.Context, checkoutDir string, bazelBinSubdirs []string, destinationDir string) error { |
| for _, subdir := range bazelBinSubdirs { |
| if err := td.Do(ctx, td.Props(fmt.Sprintf("Copying bazel-bin subdirectory %q into %q", subdir, destinationDir)), func(ctx context.Context) error { |
| srcDir := filepath.Join(checkoutDir, "bazel-bin", subdir) |
| dstDir := filepath.Join(destinationDir, subdir) |
| |
| return filepath.WalkDir(srcDir, func(path string, d fs.DirEntry, err error) error { |
| if err != nil { |
| // A non-nil err argument tells us the reason why filepath.WalkDir will not walk into |
| // that directory (see https://pkg.go.dev/io/fs#WalkDirFunc). We choose to fail loudly |
| // as this might reveal permission issues, problems with symlinks, etc. |
| return skerr.Wrap(err) |
| } |
| |
| relPath, err := filepath.Rel(srcDir, path) |
| if err != nil { |
| return skerr.Wrap(err) |
| } |
| dstPath := filepath.Join(dstDir, relPath) |
| |
| if d.IsDir() { |
| return skerr.Wrap(os_steps.MkdirAll(ctx, dstPath)) |
| } |
| return skerr.Wrap(os_steps.CopyFile(ctx, path, dstPath)) |
| }) |
| }); err != nil { |
| return skerr.Wrap(err) |
| } |
| } |
| return nil |
| } |