| // 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" |
| "path/filepath" |
| "strings" |
| |
| "go.skia.org/infra/go/common" |
| sk_exec "go.skia.org/infra/go/exec" |
| "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" |
| ) |
| |
| var ( |
| // Required properties for this task. |
| // 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 = flag.String("cache_path", "/mnt/pd0/bazel_cache", "The path where the Bazel cache should live. This should be able to hold tens of GB at least.") |
| bazelArgs = common.NewMultiStringFlag("bazel_arg", nil, "Additional arguments that should be forwarded directly to the Bazel invocation.") |
| gitPath = flag.String("git_path", "", "Location of git binary to use for diffs.") |
| 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, the root directory of a full Skia checkout") |
| // 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() { |
| // StartRun calls flag.Parse() |
| ctx := td.StartRun(projectId, taskId, taskName, output, local) |
| defer td.EndRun(ctx) |
| |
| if *gitPath == "" { |
| td.Fatal(ctx, fmt.Errorf("--git_path is required")) |
| } |
| |
| wd, err := os_steps.Abs(ctx, *workdir) |
| if err != nil { |
| td.Fatal(ctx, err) |
| } |
| absGit, err := os_steps.Abs(ctx, *gitPath) |
| if err != nil { |
| td.Fatal(ctx, err) |
| } |
| if _, err := os_steps.Stat(ctx, absGit); err != nil { |
| fmt.Printf("Cannot stat git binary %s\n", absGit) |
| td.Fatal(ctx, err) |
| } |
| skiaPath := filepath.Join(wd, "skia") |
| |
| // When running on the CI, there is not a git checkout here, so we make a temp one. |
| if !*local { |
| if err := gitInit(ctx, absGit, skiaPath); err != nil { |
| td.Fatal(ctx, err) |
| } |
| } |
| |
| opts := bazel.BazelOptions{ |
| CachePath: *cachePath, |
| } |
| if err := bazel.EnsureBazelRCFile(ctx, opts); err != nil { |
| td.Fatal(ctx, err) |
| } |
| |
| if err := bazelRun(ctx, skiaPath, "//bazel/deps_parser", *bazelArgs...); err != nil { |
| td.Fatal(ctx, err) |
| } |
| |
| if err := bazelRun(ctx, skiaPath, "//tools/gpu/gl/interface:generate_gl_interfaces", *bazelArgs...); err != nil { |
| td.Fatal(ctx, err) |
| } |
| |
| skslTests := []string{ |
| "compile_glsl_tests", |
| "compile_glsl_nosettings_tests", |
| "compile_metal_tests", |
| "compile_skrp_tests", |
| "compile_skvm_tests", |
| "compile_stage_tests", |
| "compile_spirv_tests", |
| "compile_wgsl_tests", |
| } |
| for _, label := range skslTests { |
| if err := bazelRun(ctx, skiaPath, "//tools/skslc:"+label, *bazelArgs...); err != nil { |
| td.Fatal(ctx, err) |
| } |
| } |
| |
| if err := bazelRun(ctx, skiaPath, "//tools:generate_workarounds", *bazelArgs...); err != nil { |
| td.Fatal(ctx, err) |
| } |
| |
| if err := bazelRun(ctx, skiaPath, "//tools/sksl-minify:minify_srcs", *bazelArgs...); err != nil { |
| td.Fatal(ctx, err) |
| } |
| |
| if err := bazelRun(ctx, skiaPath, "//tools/sksl-minify:minify_tests", *bazelArgs...); err != nil { |
| td.Fatal(ctx, err) |
| } |
| |
| if err := generateGNIFiles(ctx, skiaPath); err != nil { |
| td.Fatal(ctx, err) |
| } |
| |
| if err := checkGitDiff(ctx, absGit, skiaPath); err != nil { |
| td.Fatal(ctx, err) |
| } |
| } |
| |
| // bazelRun runs the given Bazel label from the Skia path with any given args using Bazelisk. |
| func bazelRun(ctx context.Context, skiaPath, label string, args ...string) error { |
| return td.Do(ctx, td.Props("bazel run "+label), func(ctx context.Context) error { |
| runCmd := &sk_exec.Command{ |
| Name: "bazelisk", |
| Args: append([]string{"run", label}, args...), |
| InheritEnv: true, // Need to make sure bazelisk is on the path |
| Dir: skiaPath, |
| LogStdout: true, |
| LogStderr: true, |
| } |
| _, err := sk_exec.RunCommand(ctx, runCmd) |
| if err != nil { |
| return err |
| } |
| return nil |
| }) |
| } |
| |
| // gitInit creates a temporary git repository with all files in the Skia path. This allows the later |
| // git diff call to work properly. This is necessary because our current Swarming setup does not |
| // include the .git folder when copying down files. |
| func gitInit(ctx context.Context, gitPath, skiaPath string) error { |
| step := fmt.Sprintf("Setting git baseline in %s", skiaPath) |
| err := td.Do(ctx, td.Props(step), func(ctx context.Context) error { |
| initCmd := &sk_exec.Command{ |
| Name: gitPath, |
| Args: []string{"init"}, |
| InheritEnv: false, |
| Dir: skiaPath, |
| LogStdout: true, |
| LogStderr: true, |
| } |
| if _, err := sk_exec.RunCommand(ctx, initCmd); err != nil { |
| return err |
| } |
| addCmd := &sk_exec.Command{ |
| Name: gitPath, |
| Args: []string{"add", "."}, |
| InheritEnv: false, |
| Dir: skiaPath, |
| LogStdout: true, |
| LogStderr: true, |
| } |
| if _, err := sk_exec.RunCommand(ctx, addCmd); err != nil { |
| return err |
| } |
| commitCmd := &sk_exec.Command{ |
| Name: gitPath, |
| Args: []string{"commit", "-m", "baseline commit"}, |
| InheritEnv: false, |
| Dir: skiaPath, |
| LogStdout: true, |
| LogStderr: true, |
| } |
| if _, err := sk_exec.RunCommand(ctx, commitCmd); err != nil { |
| return err |
| } |
| return nil |
| }) |
| return err |
| } |
| |
| // generateGNIFiles re-generates the .gni files from BUILD.bazel files, allowing interopt between |
| // the new system (Bazel) and the old one GN. |
| func generateGNIFiles(ctx context.Context, skiaPath string) error { |
| return td.Do(ctx, td.Props("Generate GNI files from BUILD.bazel ones"), func(ctx context.Context) error { |
| // Note: This is not done with bazel run ... because the exporter_tool calls Bazel, causing |
| // an apparent deadlock because there can only be one running bazel task at a time. |
| runCmd := &sk_exec.Command{ |
| Name: "make", |
| Args: []string{"-C", "bazel", "generate_gni_rbe"}, |
| InheritEnv: true, // Need to make sure bazelisk is on the path, |
| Dir: skiaPath, |
| LogStdout: true, |
| LogStderr: true, |
| } |
| if _, err := sk_exec.RunCommand(ctx, runCmd); err != nil { |
| return err |
| } |
| return nil |
| }) |
| } |
| |
| // checkGitDiff runs git diff and returns error if the diff is non-empty. |
| func checkGitDiff(ctx context.Context, gitPath, skiaPath string) error { |
| step := fmt.Sprintf("git diff %s", skiaPath) |
| return td.Do(ctx, td.Props(step), func(ctx context.Context) error { |
| runCmd := &sk_exec.Command{ |
| Name: gitPath, |
| Args: []string{"diff", "--no-ext-diff"}, |
| InheritEnv: false, |
| Dir: skiaPath, |
| LogStdout: true, |
| LogStderr: true, |
| } |
| rv, err := sk_exec.RunCommand(ctx, runCmd) |
| if err != nil { |
| return err |
| } |
| if strings.TrimSpace(rv) != "" { |
| return fmt.Errorf("Non-empty diff:\n" + rv) |
| } |
| return nil |
| }) |
| } |