blob: 81365128c8e302c449b2e4b8a8b112f1d0fd9e02 [file] [log] [blame]
// 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
})
}