xref: /aosp_15_r20/external/skia/bazel/device_specific_configs/generate/generate.go (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 program generates file //bazel/devicesrc.
7
8//go:generate bazelisk run //bazel/device_specific_configs/generate -- --output-file ${PWD}/../../devicesrc
9
10package main
11
12import (
13	"flag"
14	"fmt"
15	"os"
16	"sort"
17	"strings"
18
19	"go.skia.org/skia/bazel/device_specific_configs"
20)
21
22const header = "# GENERATED FILE - Please do not edit.\n\n"
23
24func writeFlag(sb *strings.Builder, configName, flagName, flagValue string) {
25	_, _ = sb.WriteString(fmt.Sprintf("test:%s %s=%s\n", configName, flagName, flagValue))
26}
27
28func writeTestArgFlag(sb *strings.Builder, configName, testArgFlag string) {
29	_, _ = sb.WriteString(fmt.Sprintf("test:%s --test_arg=%s\n", configName, testArgFlag))
30}
31
32func writeDeviceFlagsFile(outputFile string) error {
33	// Sort for determinism.
34	var configNames []string
35	for configName := range device_specific_configs.Configs {
36		configNames = append(configNames, configName)
37	}
38	sort.Strings(configNames)
39
40	var sb strings.Builder
41	_, _ = sb.WriteString(header)
42
43	for i, configName := range configNames {
44		if i > 0 {
45			sb.WriteString("\n")
46		}
47
48		// Force device-specific tests to run locally (as opposed to on RBE). For such tests, we assume
49		// Bazel is running on the device under test, or on a machine that controls the device under
50		// test (e.g. an Android device attached via USB). Compilation still happens on RBE.
51		//
52		// We force local execution via the --strategy flag[1]. In order to understand the --strategy
53		// flag, we must first understand the --spawn_strategy flag[2], which controls where and how
54		// commands are executed. For example:
55		//
56		//  - Flag --spawn_strategy=sandboxed executes commands inside a sandbox on the local system.
57		//    This is the default Bazel behavior on systems that support sandboxing.
58		//
59		//  - Flag --spawn_strategy=local executes commands as regular, local subprocesses without any
60		//    sandboxing.
61		//
62		//  - Flag --spawn_strategy=remote executes commands remotely, provided a remote executor has
63		//    been configured. We use this strategy when running Bazel with --config=remote. See the
64		//    //.bazelrc file[3].
65		//
66		// The --strategy flag allows us to override --spawn_strategy on a per-mnemonic basis. In our
67		// case, we set --strategy=TestRunner=local to force test actions to run as a local subprocess.
68		// In combination with --config=remote (or any configuration that implies it, such as
69		// --config=linux_rbe) this has the effect of running test actions locally, while build actions
70		// (and any other actions) run on RBE.
71		//
72		// The "TestRunner" mnemonic for test actions is determined here[4].
73		//
74		// [1] https://bazel.build/docs/user-manual#strategy
75		// [2] https://bazel.build/docs/user-manual#spawn-strategy
76		// [3] https://skia.googlesource.com/skia/+/e5c37860c792de6bba0c9465c3f5280cb13dbbb9/.bazelrc#128
77		// [4] https://github.com/bazelbuild/bazel/blob/f79ca0275e14d7c8fb478bd910ad7fb127440fd8/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java#L107
78		writeFlag(&sb, configName, "--strategy", "TestRunner=local")
79
80		config := device_specific_configs.Configs[configName]
81		for _, arg := range config.TestRunnerArgs() {
82			writeTestArgFlag(&sb, configName, arg)
83		}
84	}
85
86	return os.WriteFile(outputFile, []byte(sb.String()), 0644)
87}
88
89func main() {
90	outputFileFlag := flag.String("output-file", "", "Path to the output file.")
91	flag.Parse()
92
93	if *outputFileFlag == "" {
94		fmt.Println("Flag --output-file is required.")
95		os.Exit(1)
96	}
97
98	if err := writeDeviceFlagsFile(*outputFileFlag); err != nil {
99		fmt.Printf("error: %s\n", err)
100		os.Exit(1)
101	}
102}
103