Merge "Pass declarations as separate arguments" into main
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 130336b..4d91cc8 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -16,6 +16,8 @@
import (
"bytes"
+ "crypto/sha1"
+ "encoding/hex"
"fmt"
"os"
"path"
@@ -1222,7 +1224,11 @@
ctx.AddNinjaFileDeps(file)
}
+ depsetHashToDepset := map[string]bazel.AqueryDepset{}
+
for _, depset := range ctx.Config().BazelContext.AqueryDepsets() {
+ depsetHashToDepset[depset.ContentHash] = depset
+
var outputs []Path
var orderOnlies []Path
for _, depsetDepHash := range depset.TransitiveDepSetHashes {
@@ -1257,7 +1263,30 @@
}
if len(buildStatement.Command) > 0 {
rule := NewRuleBuilder(pctx, ctx)
- createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
+ intermediateDir, intermediateDirHash := intermediatePathForSboxMixedBuildAction(ctx, buildStatement)
+ if buildStatement.ShouldRunInSbox {
+ // Create a rule to build the output inside a sandbox
+ // This will create two changes of working directory
+ // 1. From ANDROID_BUILD_TOP to sbox top
+ // 2. From sbox top to a a synthetic mixed build execution root relative to it
+ // Finally, the outputs will be copied to intermediateDir
+ rule.Sbox(intermediateDir,
+ PathForOutput(ctx, "mixed_build_sbox_intermediates", intermediateDirHash+".textproto")).
+ SandboxInputs().
+ // Since we will cd to mixed build execution root, set sbox's out subdir to empty
+ // Without this, we will try to copy from $SBOX_SANDBOX_DIR/out/out/bazel/output/execroot/__main__/...
+ SetSboxOutDirDirAsEmpty()
+
+ // Create another set of rules to copy files from the intermediate dir to mixed build execution root
+ for _, outputPath := range buildStatement.OutputPaths {
+ ctx.Build(pctx, BuildParams{
+ Rule: CpIfChanged,
+ Input: intermediateDir.Join(ctx, executionRoot, outputPath),
+ Output: PathForBazelOut(ctx, outputPath),
+ })
+ }
+ }
+ createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx, depsetHashToDepset)
desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
rule.Build(fmt.Sprintf("bazel %d", index), desc)
continue
@@ -1304,10 +1333,25 @@
}
}
+// Returns a out dir path for a sandboxed mixed build action
+func intermediatePathForSboxMixedBuildAction(ctx PathContext, statement *bazel.BuildStatement) (OutputPath, string) {
+ // An artifact can be generated by a single buildstatement.
+ // Use the hash of the first artifact to create a unique path
+ uniqueDir := sha1.New()
+ uniqueDir.Write([]byte(statement.OutputPaths[0]))
+ uniqueDirHashString := hex.EncodeToString(uniqueDir.Sum(nil))
+ return PathForOutput(ctx, "mixed_build_sbox_intermediates", uniqueDirHashString), uniqueDirHashString
+}
+
// Register bazel-owned build statements (obtained from the aquery invocation).
-func createCommand(cmd *RuleBuilderCommand, buildStatement *bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx BuilderContext) {
+func createCommand(cmd *RuleBuilderCommand, buildStatement *bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx BuilderContext, depsetHashToDepset map[string]bazel.AqueryDepset) {
// executionRoot is the action cwd.
- cmd.Text(fmt.Sprintf("cd '%s' &&", executionRoot))
+ if buildStatement.ShouldRunInSbox {
+ // mkdir -p ensures that the directory exists when run via sbox
+ cmd.Text(fmt.Sprintf("mkdir -p '%s' && cd '%s' &&", executionRoot, executionRoot))
+ } else {
+ cmd.Text(fmt.Sprintf("cd '%s' &&", executionRoot))
+ }
// Remove old outputs, as some actions might not rerun if the outputs are detected.
if len(buildStatement.OutputPaths) > 0 {
@@ -1334,7 +1378,16 @@
}
for _, outputPath := range buildStatement.OutputPaths {
- cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
+ if buildStatement.ShouldRunInSbox {
+ // The full path has three components that get joined together
+ // 1. intermediate output dir that `sbox` will place the artifacts at
+ // 2. mixed build execution root
+ // 3. artifact path returned by aquery
+ intermediateDir, _ := intermediatePathForSboxMixedBuildAction(ctx, buildStatement)
+ cmd.ImplicitOutput(intermediateDir.Join(ctx, executionRoot, outputPath))
+ } else {
+ cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
+ }
}
for _, inputPath := range buildStatement.OrderOnlyInputs {
cmd.OrderOnly(PathForBazelOut(ctx, inputPath))
@@ -1343,8 +1396,15 @@
cmd.Implicit(PathForBazelOut(ctx, inputPath))
}
for _, inputDepsetHash := range buildStatement.InputDepsetHashes {
- otherDepsetName := bazelDepsetName(inputDepsetHash)
- cmd.Implicit(PathForPhony(ctx, otherDepsetName))
+ if buildStatement.ShouldRunInSbox {
+ // Bazel depsets are phony targets that are used to group files.
+ // We need to copy the grouped files into the sandbox
+ ds, _ := depsetHashToDepset[inputDepsetHash]
+ cmd.Implicits(PathsForBazelOut(ctx, ds.DirectArtifacts))
+ } else {
+ otherDepsetName := bazelDepsetName(inputDepsetHash)
+ cmd.Implicit(PathForPhony(ctx, otherDepsetName))
+ }
}
if depfile := buildStatement.Depfile; depfile != nil {
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index 65cd5a8..e08a471 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -181,13 +181,62 @@
cmd := RuleBuilderCommand{}
ctx := builderContextForTests{PathContextForTesting(TestConfig("out", nil, "", nil))}
- createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", ctx)
+ createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", ctx, map[string]bazel.AqueryDepset{})
if actual, expected := cmd.buf.String(), testCase.command; expected != actual {
t.Errorf("expected: [%s], actual: [%s]", expected, actual)
}
}
}
+func TestMixedBuildSandboxedAction(t *testing.T) {
+ input := `{
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 1 },
+ { "id": 2, "path_fragment_id": 2 }],
+ "actions": [{
+ "target_Id": 1,
+ "action_Key": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "input_dep_set_ids": [1],
+ "output_Ids": [1],
+ "primary_output_id": 1
+ }],
+ "dep_set_of_files": [
+ { "id": 1, "direct_artifact_ids": [1, 2] }],
+ "path_fragments": [
+ { "id": 1, "label": "one" },
+ { "id": 2, "label": "two" }]
+}`
+ data, err := JsonToActionGraphContainer(input)
+ if err != nil {
+ t.Error(err)
+ }
+ bazelContext, _ := testBazelContext(t, map[bazelCommand]string{aqueryCmd: string(data)})
+
+ err = bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
+ if err != nil {
+ t.Fatalf("TestMixedBuildSandboxedAction did not expect error invoking Bazel, but got %s", err)
+ }
+
+ statement := bazelContext.BuildStatementsToRegister()[0]
+ statement.ShouldRunInSbox = true
+
+ cmd := RuleBuilderCommand{}
+ ctx := builderContextForTests{PathContextForTesting(TestConfig("out", nil, "", nil))}
+ createCommand(&cmd, statement, "test/exec_root", "test/bazel_out", ctx, map[string]bazel.AqueryDepset{})
+ // Assert that the output is generated in an intermediate directory
+ // fe05bcdcdc4928012781a5f1a2a77cbb5398e106 is the sha1 checksum of "one"
+ if actual, expected := cmd.outputs[0].String(), "out/soong/mixed_build_sbox_intermediates/fe05bcdcdc4928012781a5f1a2a77cbb5398e106/test/exec_root/one"; expected != actual {
+ t.Errorf("expected: [%s], actual: [%s]", expected, actual)
+ }
+
+ // Assert the actual command remains unchanged inside the sandbox
+ if actual, expected := cmd.buf.String(), "mkdir -p 'test/exec_root' && cd 'test/exec_root' && rm -rf 'one' && touch foo"; expected != actual {
+ t.Errorf("expected: [%s], actual: [%s]", expected, actual)
+ }
+}
+
func TestCoverageFlagsAfterInvokeBazel(t *testing.T) {
testConfig.productVariables.ClangCoverage = boolPtr(true)
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 0438eb8..777c1cf 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -53,6 +53,7 @@
remoteable RemoteRuleSupports
rbeParams *remoteexec.REParams
outDir WritablePath
+ sboxOutSubDir string
sboxTools bool
sboxInputs bool
sboxManifestPath WritablePath
@@ -65,9 +66,18 @@
pctx: pctx,
ctx: ctx,
temporariesSet: make(map[WritablePath]bool),
+ sboxOutSubDir: sboxOutSubDir,
}
}
+// SetSboxOutDirDirAsEmpty sets the out subdirectory to an empty string
+// This is useful for sandboxing actions that change the execution root to a path in out/ (e.g mixed builds)
+// For such actions, SetSboxOutDirDirAsEmpty ensures that the path does not become $SBOX_SANDBOX_DIR/out/out/bazel/output/execroot/__main__/...
+func (rb *RuleBuilder) SetSboxOutDirDirAsEmpty() *RuleBuilder {
+ rb.sboxOutSubDir = ""
+ return rb
+}
+
// RuleBuilderInstall is a tuple of install from and to locations.
type RuleBuilderInstall struct {
From Path
@@ -585,7 +595,7 @@
for _, output := range outputs {
rel := Rel(r.ctx, r.outDir.String(), output.String())
command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
- From: proto.String(filepath.Join(sboxOutSubDir, rel)),
+ From: proto.String(filepath.Join(r.sboxOutSubDir, rel)),
To: proto.String(output.String()),
})
}
diff --git a/bazel/aquery.go b/bazel/aquery.go
index d4879b1..186a494 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -117,6 +117,9 @@
InputPaths []string
OrderOnlyInputs []string
FileContents string
+ // If ShouldRunInSbox is true, Soong will use sbox to created an isolated environment
+ // and run the mixed build action there
+ ShouldRunInSbox bool
}
// A helper type for aquery processing which facilitates retrieval of path IDs from their
@@ -517,6 +520,12 @@
Env: actionEntry.EnvironmentVariables,
Mnemonic: actionEntry.Mnemonic,
}
+ if buildStatement.Mnemonic == "GoToolchainBinaryBuild" {
+ // Unlike b's execution root, mixed build execution root contains a symlink to prebuilts/go
+ // This causes issues for `GOCACHE=$(mktemp -d) go build ...`
+ // To prevent this, sandbox this action in mixed builds as well
+ buildStatement.ShouldRunInSbox = true
+ }
return buildStatement, nil
}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index a817386..0e6596b 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -471,17 +471,6 @@
return []BazelTarget{binTarget}, nil
}
-var (
- // TODO - b/284483729: Remove this denyilst
- // Temporary denylist of go binaries that are currently used in mixed builds
- // This denylist allows us to rollout bp2build converters for go targets without affecting mixed builds
- goBinaryDenylist = []string{
- "soong_zip",
- "zip2zip",
- "bazel_notice_gen",
- }
-)
-
func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (conversionResults, []error) {
buildFileToTargets := make(map[string]BazelTargets)
@@ -574,7 +563,7 @@
targets, targetErrs = generateBazelTargetsGoPackage(bpCtx, glib, nameToGoLibMap)
errs = append(errs, targetErrs...)
metrics.IncrementRuleClassCount("go_library")
- } else if gbin, ok := m.(*bootstrap.GoBinary); ok && !android.InList(m.Name(), goBinaryDenylist) {
+ } else if gbin, ok := m.(*bootstrap.GoBinary); ok {
targets, targetErrs = generateBazelTargetsGoBinary(bpCtx, gbin, nameToGoLibMap)
errs = append(errs, targetErrs...)
metrics.IncrementRuleClassCount("go_binary")
diff --git a/cc/cc.go b/cc/cc.go
index 187f2d6..84b80a1 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -2950,20 +2950,20 @@
ctx.ModuleErrorf("links %q built against newer API version %q",
ctx.OtherModuleName(to.Module()), "current")
} else {
- fromApi, err := strconv.Atoi(from.SdkVersion())
+ fromApi, err := android.ApiLevelFromUserWithConfig(ctx.Config(), from.SdkVersion())
if err != nil {
ctx.PropertyErrorf("sdk_version",
- "Invalid sdk_version value (must be int or current): %q",
+ "Invalid sdk_version value (must be int, preview or current): %q",
from.SdkVersion())
}
- toApi, err := strconv.Atoi(to.SdkVersion())
+ toApi, err := android.ApiLevelFromUserWithConfig(ctx.Config(), to.SdkVersion())
if err != nil {
ctx.PropertyErrorf("sdk_version",
- "Invalid sdk_version value (must be int or current): %q",
+ "Invalid sdk_version value (must be int, preview or current): %q",
to.SdkVersion())
}
- if toApi > fromApi {
+ if toApi.GreaterThan(fromApi) {
ctx.ModuleErrorf("links %q built against newer API version %q",
ctx.OtherModuleName(to.Module()), to.SdkVersion())
}
diff --git a/cmd/zip2zip/BUILD.bazel b/cmd/zip2zip/BUILD.bazel
deleted file mode 100644
index 1915a2d..0000000
--- a/cmd/zip2zip/BUILD.bazel
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (C) 2022 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-alias(
- name = "zip2zip",
- actual = "//prebuilts/build-tools:linux-x86/bin/zip2zip",
-)
diff --git a/zip/cmd/BUILD.bazel b/zip/cmd/BUILD.bazel
deleted file mode 100644
index e04a1e1..0000000
--- a/zip/cmd/BUILD.bazel
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (C) 2022 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# TODO(b/194644518): Switch to the source version when Bazel can build go
-# binaries.
-alias(
- name = "soong_zip",
- actual = "//prebuilts/build-tools:linux-x86/bin/soong_zip",
-)