Merge "allow arch_variants for C++'s rtti option" into main
diff --git a/android/buildinfo_prop.go b/android/buildinfo_prop.go
index 8288fc5..083f3ef 100644
--- a/android/buildinfo_prop.go
+++ b/android/buildinfo_prop.go
@@ -23,7 +23,7 @@
func init() {
ctx := InitRegistrationContext
- ctx.RegisterParallelSingletonModuleType("buildinfo_prop", buildinfoPropFactory)
+ ctx.RegisterModuleType("buildinfo_prop", buildinfoPropFactory)
}
type buildinfoPropProperties struct {
@@ -32,7 +32,7 @@
}
type buildinfoPropModule struct {
- SingletonModuleBase
+ ModuleBase
properties buildinfoPropProperties
@@ -88,6 +88,10 @@
}
func (p *buildinfoPropModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ if ctx.ModuleName() != "buildinfo.prop" || ctx.ModuleDir() != "build/soong" {
+ ctx.ModuleErrorf("There can only be one buildinfo_prop module in build/soong")
+ return
+ }
p.outputFilePath = PathForModuleOut(ctx, p.Name()).OutputPath
if !ctx.Config().KatiEnabled() {
WriteFileRule(ctx, p.outputFilePath, "# no buildinfo.prop if kati is disabled")
@@ -111,10 +115,11 @@
cmd.FlagWithArg("--build-id=", config.BuildId())
cmd.FlagWithArg("--build-keys=", config.BuildKeys())
- // shouldn't depend on BuildNumberFile and BuildThumbprintFile to prevent from rebuilding
- // on every incremental build.
- cmd.FlagWithArg("--build-number-file=", config.BuildNumberFile(ctx).String())
+ // Note: depending on BuildNumberFile will cause the build.prop file to be rebuilt
+ // every build, but that's intentional.
+ cmd.FlagWithInput("--build-number-file=", config.BuildNumberFile(ctx))
if shouldAddBuildThumbprint(config) {
+ // In the previous make implementation, a dependency was not added on the thumbprint file
cmd.FlagWithArg("--build-thumbprint-file=", config.BuildThumbprintFile(ctx).String())
}
@@ -123,8 +128,10 @@
cmd.FlagWithArg("--build-variant=", buildVariant)
cmd.FlagForEachArg("--cpu-abis=", config.DeviceAbi())
- // shouldn't depend on BUILD_DATETIME_FILE to prevent from rebuilding on every incremental
- // build.
+ // Technically we should also have a dependency on BUILD_DATETIME_FILE,
+ // but it can be either an absolute or relative path, which is hard to turn into
+ // a Path object. So just rely on the BuildNumberFile always changing to cause
+ // us to rebuild.
cmd.FlagWithArg("--date-file=", ctx.Config().Getenv("BUILD_DATETIME_FILE"))
if len(config.ProductLocales()) > 0 {
@@ -163,12 +170,8 @@
ctx.InstallFile(p.installPath, p.Name(), p.outputFilePath)
}
-func (f *buildinfoPropModule) GenerateSingletonBuildActions(ctx SingletonContext) {
- // does nothing; buildinfo_prop is a singeton because two buildinfo modules don't make sense.
-}
-
func (p *buildinfoPropModule) AndroidMkEntries() []AndroidMkEntries {
- return []AndroidMkEntries{AndroidMkEntries{
+ return []AndroidMkEntries{{
Class: "ETC",
OutputFile: OptionalPathForPath(p.outputFilePath),
ExtraEntries: []AndroidMkExtraEntriesFunc{
@@ -184,7 +187,7 @@
// buildinfo_prop module generates a build.prop file, which contains a set of common
// system/build.prop properties, such as ro.build.version.*. Not all properties are implemented;
// currently this module is only for microdroid.
-func buildinfoPropFactory() SingletonModule {
+func buildinfoPropFactory() Module {
module := &buildinfoPropModule{}
module.AddProperties(&module.properties)
InitAndroidModule(module)
diff --git a/android/config.go b/android/config.go
index c8e51ab..a18cb8b 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1334,10 +1334,6 @@
return c.productVariables.SourceRootDirs
}
-func (c *config) IncludeTags() []string {
- return c.productVariables.IncludeTags
-}
-
func (c *config) HostStaticBinaries() bool {
return Bool(c.productVariables.HostStaticBinaries)
}
diff --git a/android/gen_notice.go b/android/gen_notice.go
index 6815f64..9adde9e 100644
--- a/android/gen_notice.go
+++ b/android/gen_notice.go
@@ -176,6 +176,7 @@
}
out := m.getStem() + m.getSuffix()
m.output = PathForModuleOut(ctx, out).OutputPath
+ ctx.SetOutputFiles(Paths{m.output}, "")
}
func GenNoticeFactory() Module {
@@ -193,16 +194,6 @@
return module
}
-var _ OutputFileProducer = (*genNoticeModule)(nil)
-
-// Implements OutputFileProducer
-func (m *genNoticeModule) OutputFiles(tag string) (Paths, error) {
- if tag == "" {
- return Paths{m.output}, nil
- }
- return nil, fmt.Errorf("unrecognized tag %q", tag)
-}
-
var _ AndroidMkEntriesProvider = (*genNoticeModule)(nil)
// Implements AndroidMkEntriesProvider
diff --git a/android/module_context.go b/android/module_context.go
index bc08911..591e270 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -718,6 +718,9 @@
}
m.module.base().outputFiles.DefaultOutputFiles = outputFiles
} else {
+ if m.module.base().outputFiles.TaggedOutputFiles == nil {
+ m.module.base().outputFiles.TaggedOutputFiles = make(map[string]Paths)
+ }
if _, exists := m.module.base().outputFiles.TaggedOutputFiles[tag]; exists {
m.ModuleErrorf("Module %s OutputFiles at tag %s cannot be overwritten", m.ModuleName(), tag)
} else {
diff --git a/android/register.go b/android/register.go
index aeb3b4c..eb6a35e 100644
--- a/android/register.go
+++ b/android/register.go
@@ -156,7 +156,6 @@
func NewContext(config Config) *Context {
ctx := &Context{blueprint.NewContext(), config}
ctx.SetSrcDir(absSrcDir)
- ctx.AddIncludeTags(config.IncludeTags()...)
ctx.AddSourceRootDirs(config.SourceRootDirs()...)
return ctx
}
diff --git a/android/variable.go b/android/variable.go
index b939acc..1633816 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -476,7 +476,6 @@
IgnorePrefer32OnDevice bool `json:",omitempty"`
- IncludeTags []string `json:",omitempty"`
SourceRootDirs []string `json:",omitempty"`
AfdoProfiles []string `json:",omitempty"`
diff --git a/bin/Android.bp b/bin/Android.bp
new file mode 100644
index 0000000..4d6d911
--- /dev/null
+++ b/bin/Android.bp
@@ -0,0 +1,26 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+ default_team: "trendy_team_build",
+}
+
+filegroup {
+ name: "run_tool_with_logging_script",
+ visibility: [
+ "//build/soong/tests:__subpackages__",
+ ],
+ srcs: ["run_tool_with_logging"],
+}
diff --git a/bin/run_tool_with_logging b/bin/run_tool_with_logging
new file mode 100755
index 0000000..2b2c8d8
--- /dev/null
+++ b/bin/run_tool_with_logging
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+# Copyright (C) 2024 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.
+
+# Run commands in a subshell for us to handle forced terminations with a trap
+# handler.
+(
+tool_tag="$1"
+shift
+tool_binary="$1"
+shift
+
+# If the logger is not configured, run the original command and return.
+if [[ -z "${ANDROID_TOOL_LOGGER}" ]]; then
+ "${tool_binary}" "${@}"
+ exit $?
+fi
+
+# Otherwise, run the original command and call the logger when done.
+start_time=$(date +%s.%N)
+logger=${ANDROID_TOOL_LOGGER}
+
+# Install a trap to call the logger even when the process terminates abnormally.
+# The logger is run in the background and its output suppressed to avoid
+# interference with the user flow.
+trap '
+exit_code=$?;
+# Remove the trap to prevent duplicate log.
+trap - EXIT;
+"${logger}" \
+ --tool_tag="${tool_tag}" \
+ --start_timestamp="${start_time}" \
+ --end_timestamp="$(date +%s.%N)" \
+ --tool_args="$*" \
+ --exit_code="${exit_code}" \
+ ${ANDROID_TOOL_LOGGER_EXTRA_ARGS} \
+ > /dev/null 2>&1 &
+exit ${exit_code}
+' SIGINT SIGTERM SIGQUIT EXIT
+
+# Run the original command.
+"${tool_binary}" "${@}"
+)
diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go
index 02eedc8..052cde8 100644
--- a/cmd/release_config/release_config_lib/release_configs.go
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -87,16 +87,15 @@
data := []string{}
usedAliases := make(map[string]bool)
priorStages := make(map[string][]string)
- rankedStageNames := make(map[string]bool)
for _, config := range configs.ReleaseConfigs {
+ if config.Name == "root" {
+ continue
+ }
var fillColor string
inherits := []string{}
for _, inherit := range config.InheritNames {
if inherit == "root" {
- // Only show "root" if we have no other inheritance.
- if len(config.InheritNames) > 1 {
- continue
- }
+ continue
}
data = append(data, fmt.Sprintf(`"%s" -> "%s"`, config.Name, inherit))
inherits = append(inherits, inherit)
@@ -113,14 +112,9 @@
}
// Add links for all of the advancement progressions.
for priorStage := range config.PriorStagesMap {
- stageName := config.Name
- if len(config.OtherNames) > 0 {
- stageName = config.OtherNames[0]
- }
data = append(data, fmt.Sprintf(`"%s" -> "%s" [ style=dashed color="#81c995" ]`,
- priorStage, stageName))
- priorStages[stageName] = append(priorStages[stageName], priorStage)
- rankedStageNames[stageName] = true
+ priorStage, config.Name))
+ priorStages[config.Name] = append(priorStages[config.Name], priorStage)
}
label := config.Name
if len(inherits) > 0 {
@@ -129,16 +123,24 @@
if len(config.OtherNames) > 0 {
label += "\\nother names: " + strings.Join(config.OtherNames, " ")
}
- // The active release config has a light blue fill.
- if config.Name == *configs.Artifact.ReleaseConfig.Name {
+ switch config.Name {
+ case *configs.Artifact.ReleaseConfig.Name:
+ // The active release config has a light blue fill.
fillColor = `fillcolor="#d2e3fc" `
+ case "trunk", "trunk_staging":
+ // Certain workflow stages have a light green fill.
+ fillColor = `fillcolor="#ceead6" `
+ default:
+ // Look for "next" and "*_next", make them light green as well.
+ for _, n := range config.OtherNames {
+ if n == "next" || strings.HasSuffix(n, "_next") {
+ fillColor = `fillcolor="#ceead6" `
+ }
+ }
}
data = append(data,
fmt.Sprintf(`"%s" [ label="%s" %s]`, config.Name, label, fillColor))
}
- if len(rankedStageNames) > 0 {
- data = append(data, fmt.Sprintf("subgraph {rank=same %s}", strings.Join(SortedMapKeys(rankedStageNames), " ")))
- }
slices.Sort(data)
data = append([]string{
"digraph {",
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 4490dd2..3dac8bd 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -98,7 +98,6 @@
ctx := android.NewContext(configuration)
ctx.SetNameInterface(newNameResolver(configuration))
ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
- ctx.AddIncludeTags(configuration.IncludeTags()...)
ctx.AddSourceRootDirs(configuration.SourceRootDirs()...)
return ctx
}
diff --git a/filesystem/avb_gen_vbmeta_image.go b/filesystem/avb_gen_vbmeta_image.go
index 985f0ea..a7fd782 100644
--- a/filesystem/avb_gen_vbmeta_image.go
+++ b/filesystem/avb_gen_vbmeta_image.go
@@ -81,6 +81,8 @@
a.output = android.PathForModuleOut(ctx, a.installFileName()).OutputPath
cmd.FlagWithOutput("--output_vbmeta_image ", a.output)
builder.Build("avbGenVbmetaImage", fmt.Sprintf("avbGenVbmetaImage %s", ctx.ModuleName()))
+
+ ctx.SetOutputFiles([]android.Path{a.output}, "")
}
var _ android.AndroidMkEntriesProvider = (*avbGenVbmetaImage)(nil)
@@ -99,16 +101,6 @@
}}
}
-var _ android.OutputFileProducer = (*avbGenVbmetaImage)(nil)
-
-// Implements android.OutputFileProducer
-func (a *avbGenVbmetaImage) OutputFiles(tag string) (android.Paths, error) {
- if tag == "" {
- return []android.Path{a.output}, nil
- }
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
-
type avbGenVbmetaImageDefaults struct {
android.ModuleBase
android.DefaultsModuleBase
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 352b451..e796ab9 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -123,6 +123,8 @@
b.installDir = android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(b.installDir, b.installFileName(), b.output)
+
+ ctx.SetOutputFiles([]android.Path{b.output}, "")
}
func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android.OutputPath {
@@ -292,13 +294,3 @@
}
return nil
}
-
-var _ android.OutputFileProducer = (*bootimg)(nil)
-
-// Implements android.OutputFileProducer
-func (b *bootimg) OutputFiles(tag string) (android.Paths, error) {
- if tag == "" {
- return []android.Path{b.output}, nil
- }
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index d2572c2..c889dd6 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -221,6 +221,8 @@
f.installDir = android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(f.installDir, f.installFileName(), f.output)
+
+ ctx.SetOutputFiles([]android.Path{f.output}, "")
}
func validatePartitionType(ctx android.ModuleContext, p partition) {
@@ -561,16 +563,6 @@
}}
}
-var _ android.OutputFileProducer = (*filesystem)(nil)
-
-// Implements android.OutputFileProducer
-func (f *filesystem) OutputFiles(tag string) (android.Paths, error) {
- if tag == "" {
- return []android.Path{f.output}, nil
- }
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
-
// Filesystem is the public interface for the filesystem struct. Currently, it's only for the apex
// package to have access to the output file.
type Filesystem interface {
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
index e2f7d7b..e483fe4 100644
--- a/filesystem/logical_partition.go
+++ b/filesystem/logical_partition.go
@@ -185,6 +185,8 @@
l.installDir = android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(l.installDir, l.installFileName(), l.output)
+
+ ctx.SetOutputFiles([]android.Path{l.output}, "")
}
// Add a rule that converts the filesystem for the given partition to the given rule builder. The
@@ -231,13 +233,3 @@
func (l *logicalPartition) SignedOutputPath() android.Path {
return nil // logical partition is not signed by itself
}
-
-var _ android.OutputFileProducer = (*logicalPartition)(nil)
-
-// Implements android.OutputFileProducer
-func (l *logicalPartition) OutputFiles(tag string) (android.Paths, error) {
- if tag == "" {
- return []android.Path{l.output}, nil
- }
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
diff --git a/filesystem/raw_binary.go b/filesystem/raw_binary.go
index 1544ea7..ad36c29 100644
--- a/filesystem/raw_binary.go
+++ b/filesystem/raw_binary.go
@@ -15,8 +15,6 @@
package filesystem
import (
- "fmt"
-
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -88,6 +86,8 @@
r.output = outputFile
r.installDir = android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(r.installDir, r.installFileName(), r.output)
+
+ ctx.SetOutputFiles([]android.Path{r.output}, "")
}
var _ android.AndroidMkEntriesProvider = (*rawBinary)(nil)
@@ -109,13 +109,3 @@
func (r *rawBinary) SignedOutputPath() android.Path {
return nil
}
-
-var _ android.OutputFileProducer = (*rawBinary)(nil)
-
-// Implements android.OutputFileProducer
-func (r *rawBinary) OutputFiles(tag string) (android.Paths, error) {
- if tag == "" {
- return []android.Path{r.output}, nil
- }
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
index 43a2f37..0c6e7f4 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -211,6 +211,8 @@
v.installDir = android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(v.installDir, v.installFileName(), v.output)
+
+ ctx.SetOutputFiles([]android.Path{v.output}, "")
}
// Returns the embedded shell command that prints the rollback index
@@ -288,13 +290,3 @@
func (v *vbmeta) SignedOutputPath() android.Path {
return v.OutputPath() // vbmeta is always signed
}
-
-var _ android.OutputFileProducer = (*vbmeta)(nil)
-
-// Implements android.OutputFileProducer
-func (v *vbmeta) OutputFiles(tag string) (android.Paths, error) {
- if tag == "" {
- return []android.Path{v.output}, nil
- }
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 5ca6c25..b32b754 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -1054,8 +1054,7 @@
}
if !treatDocumentationIssuesAsErrors {
- // Treat documentation issues as warnings, but error when new.
- cmd.Flag("--error-when-new-category").Flag("Documentation")
+ treatDocumentationIssuesAsWarningErrorWhenNew(cmd)
}
// Add "check released" options. (Detect incompatible API changes from the last public release)
@@ -1083,6 +1082,22 @@
}
}
+// HIDDEN_DOCUMENTATION_ISSUES is the set of documentation related issues that should always be
+// hidden as they are very noisy and provide little value.
+var HIDDEN_DOCUMENTATION_ISSUES = []string{
+ "Deprecated",
+ "IntDef",
+ "Nullable",
+}
+
+func treatDocumentationIssuesAsWarningErrorWhenNew(cmd *android.RuleBuilderCommand) {
+ // Treat documentation issues as warnings, but error when new.
+ cmd.Flag("--error-when-new-category").Flag("Documentation")
+
+ // Hide some documentation issues that generated a lot of noise for little benefit.
+ cmd.FlagForEachArg("--hide ", HIDDEN_DOCUMENTATION_ISSUES)
+}
+
// Sandbox rule for generating exportable stubs and other artifacts
func (d *Droidstubs) exportableStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) {
optionalCmdParams := stubsCommandParams{
@@ -1154,7 +1169,7 @@
}
// Treat documentation issues as warnings, but error when new.
- cmd.Flag("--error-when-new-category").Flag("Documentation")
+ treatDocumentationIssuesAsWarningErrorWhenNew(cmd)
if params.stubConfig.generateStubs {
rule.Command().
diff --git a/tests/Android.bp b/tests/Android.bp
new file mode 100644
index 0000000..458cf4b
--- /dev/null
+++ b/tests/Android.bp
@@ -0,0 +1,34 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+ default_team: "trendy_team_build",
+}
+
+python_test_host {
+ name: "run_tool_with_logging_test",
+ main: "run_tool_with_logging_test.py",
+ pkg_path: "testdata",
+ srcs: [
+ "run_tool_with_logging_test.py",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+ data: [
+ ":run_tool_with_logging_script",
+ ":tool_event_logger",
+ ],
+}
diff --git a/tests/run_tool_with_logging_test.py b/tests/run_tool_with_logging_test.py
new file mode 100644
index 0000000..57a6d62
--- /dev/null
+++ b/tests/run_tool_with_logging_test.py
@@ -0,0 +1,337 @@
+# Copyright 2024 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.
+
+import dataclasses
+import glob
+from importlib import resources
+import logging
+import os
+from pathlib import Path
+import re
+import shutil
+import signal
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import time
+import unittest
+
+EXII_RETURN_CODE = 0
+INTERRUPTED_RETURN_CODE = 130
+
+
+class RunToolWithLoggingTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ # Configure to print logging to stdout.
+ logging.basicConfig(filename=None, level=logging.DEBUG)
+ console = logging.StreamHandler(sys.stdout)
+ logging.getLogger("").addHandler(console)
+
+ def setUp(self):
+ super().setUp()
+ self.working_dir = tempfile.TemporaryDirectory()
+ # Run all the tests from working_dir which is our temp Android build top.
+ os.chdir(self.working_dir.name)
+
+ self.logging_script_path = self._import_executable("run_tool_with_logging")
+
+ def tearDown(self):
+ self.working_dir.cleanup()
+ super().tearDown()
+
+ def test_does_not_log_when_logger_var_empty(self):
+ test_tool = TestScript.create(self.working_dir)
+
+ self._run_script_and_wait(f"""
+ export ANDROID_TOOL_LOGGER=""
+ {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+ """)
+
+ test_tool.assert_called_once_with_args("arg1 arg2")
+
+ def test_does_not_log_with_logger_unset(self):
+ test_tool = TestScript.create(self.working_dir)
+
+ self._run_script_and_wait(f"""
+ unset ANDROID_TOOL_LOGGER
+ {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+ """)
+
+ test_tool.assert_called_once_with_args("arg1 arg2")
+
+ def test_log_success_with_logger_enabled(self):
+ test_tool = TestScript.create(self.working_dir)
+ test_logger = TestScript.create(self.working_dir)
+
+ self._run_script_and_wait(f"""
+ export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+ {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+ """)
+
+ test_tool.assert_called_once_with_args("arg1 arg2")
+ expected_logger_args = (
+ "--tool_tag=FAKE_TOOL --start_timestamp=\d+\.\d+ --end_timestamp="
+ "\d+\.\d+ --tool_args=arg1 arg2 --exit_code=0"
+ )
+ test_logger.assert_called_once_with_args(expected_logger_args)
+
+ def test_run_tool_output_is_same_with_and_without_logging(self):
+ test_tool = TestScript.create(self.working_dir, "echo 'tool called'")
+ test_logger = TestScript.create(self.working_dir)
+
+ run_tool_with_logging_stdout, run_tool_with_logging_stderr = (
+ self._run_script_and_wait(f"""
+ export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+ {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+ """)
+ )
+
+ run_tool_without_logging_stdout, run_tool_without_logging_stderr = (
+ self._run_script_and_wait(f"""
+ export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+ {test_tool.executable} arg1 arg2
+ """)
+ )
+
+ self.assertEqual(
+ run_tool_with_logging_stdout, run_tool_without_logging_stdout
+ )
+ self.assertEqual(
+ run_tool_with_logging_stderr, run_tool_without_logging_stderr
+ )
+
+ def test_logger_output_is_suppressed(self):
+ test_tool = TestScript.create(self.working_dir)
+ test_logger = TestScript.create(self.working_dir, "echo 'logger called'")
+
+ run_tool_with_logging_output, _ = self._run_script_and_wait(f"""
+ export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+ {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+ """)
+
+ self.assertNotIn("logger called", run_tool_with_logging_output)
+
+ def test_logger_error_is_suppressed(self):
+ test_tool = TestScript.create(self.working_dir)
+ test_logger = TestScript.create(
+ self.working_dir, "echo 'logger failed' > /dev/stderr; exit 1"
+ )
+
+ _, err = self._run_script_and_wait(f"""
+ export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+ {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+ """)
+
+ self.assertNotIn("logger failed", err)
+
+ def test_log_success_when_tool_interrupted(self):
+ test_tool = TestScript.create(self.working_dir, script_body="sleep 100")
+ test_logger = TestScript.create(self.working_dir)
+
+ process = self._run_script(f"""
+ export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+ {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+ """)
+
+ pgid = os.getpgid(process.pid)
+ # Give sometime for the subprocess to start.
+ time.sleep(1)
+ # Kill the subprocess and any processes created in the same group.
+ os.killpg(pgid, signal.SIGINT)
+
+ returncode, _, _ = self._wait_for_process(process)
+ self.assertEqual(returncode, INTERRUPTED_RETURN_CODE)
+
+ expected_logger_args = (
+ "--tool_tag=FAKE_TOOL --start_timestamp=\d+\.\d+ --end_timestamp="
+ "\d+\.\d+ --tool_args=arg1 arg2 --exit_code=130"
+ )
+ test_logger.assert_called_once_with_args(expected_logger_args)
+
+ def test_logger_can_be_toggled_on(self):
+ test_tool = TestScript.create(self.working_dir)
+ test_logger = TestScript.create(self.working_dir)
+
+ self._run_script_and_wait(f"""
+ export ANDROID_TOOL_LOGGER=""
+ export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+ {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+ """)
+
+ test_logger.assert_called_with_times(1)
+
+ def test_logger_can_be_toggled_off(self):
+ test_tool = TestScript.create(self.working_dir)
+ test_logger = TestScript.create(self.working_dir)
+
+ self._run_script_and_wait(f"""
+ export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+ export ANDROID_TOOL_LOGGER=""
+ {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+ """)
+
+ test_logger.assert_not_called()
+
+ def test_integration_tool_event_logger_dry_run(self):
+ test_tool = TestScript.create(self.working_dir)
+ logger_path = self._import_executable("tool_event_logger")
+
+ self._run_script_and_wait(f"""
+ TMPDIR="{self.working_dir.name}"
+ export ANDROID_TOOL_LOGGER="{logger_path}"
+ export ANDROID_TOOL_LOGGER_EXTRA_ARGS="--dry_run"
+ {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+ """)
+
+ self._assert_logger_dry_run()
+
+ def test_tool_args_do_not_fail_logger(self):
+ test_tool = TestScript.create(self.working_dir)
+ logger_path = self._import_executable("tool_event_logger")
+
+ self._run_script_and_wait(f"""
+ TMPDIR="{self.working_dir.name}"
+ export ANDROID_TOOL_LOGGER="{logger_path}"
+ export ANDROID_TOOL_LOGGER_EXTRA_ARGS="--dry_run"
+ {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} --tool-arg1
+ """)
+
+ self._assert_logger_dry_run()
+
+ def _import_executable(self, executable_name: str) -> Path:
+ # logger = "tool_event_logger"
+ executable_path = Path(self.working_dir.name).joinpath(executable_name)
+ with resources.as_file(
+ resources.files("testdata").joinpath(executable_name)
+ ) as p:
+ shutil.copy(p, executable_path)
+ Path.chmod(executable_path, 0o755)
+ return executable_path
+
+ def _assert_logger_dry_run(self):
+ log_files = glob.glob(self.working_dir.name + "/tool_event_logger_*/*.log")
+ self.assertEqual(len(log_files), 1)
+
+ with open(log_files[0], "r") as f:
+ lines = f.readlines()
+ self.assertEqual(len(lines), 1)
+ self.assertIn("dry run", lines[0])
+
+ def _run_script_and_wait(self, test_script: str) -> tuple[str, str]:
+ process = self._run_script(test_script)
+ returncode, out, err = self._wait_for_process(process)
+ logging.debug("script stdout: %s", out)
+ logging.debug("script stderr: %s", err)
+ self.assertEqual(returncode, EXII_RETURN_CODE)
+ return out, err
+
+ def _run_script(self, test_script: str) -> subprocess.Popen:
+ return subprocess.Popen(
+ test_script,
+ shell=True,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ text=True,
+ start_new_session=True,
+ executable="/bin/bash",
+ )
+
+ def _wait_for_process(
+ self, process: subprocess.Popen
+ ) -> tuple[int, str, str]:
+ pgid = os.getpgid(process.pid)
+ out, err = process.communicate()
+ # Wait for all process in the same group to complete since the logger runs
+ # as a separate detached process.
+ self._wait_for_process_group(pgid)
+ return (process.returncode, out, err)
+
+ def _wait_for_process_group(self, pgid: int, timeout: int = 5):
+ """Waits for all subprocesses within the process group to complete."""
+ start_time = time.time()
+ while True:
+ if time.time() - start_time > timeout:
+ raise TimeoutError(
+ f"Process group did not complete after {timeout} seconds"
+ )
+ for pid in os.listdir("/proc"):
+ if pid.isdigit():
+ try:
+ if os.getpgid(int(pid)) == pgid:
+ time.sleep(0.1)
+ break
+ except (FileNotFoundError, PermissionError, ProcessLookupError):
+ pass
+ else:
+ # All processes have completed.
+ break
+
+
+@dataclasses.dataclass
+class TestScript:
+ executable: Path
+ output_file: Path
+
+ def create(temp_dir: Path, script_body: str = ""):
+ with tempfile.NamedTemporaryFile(dir=temp_dir.name, delete=False) as f:
+ output_file = f.name
+
+ with tempfile.NamedTemporaryFile(dir=temp_dir.name, delete=False) as f:
+ executable = f.name
+ executable_contents = textwrap.dedent(f"""
+ #!/bin/bash
+
+ echo "${{@}}" >> {output_file}
+ {script_body}
+ """)
+ f.write(executable_contents.encode("utf-8"))
+
+ Path.chmod(f.name, os.stat(f.name).st_mode | stat.S_IEXEC)
+
+ return TestScript(executable, output_file)
+
+ def assert_called_with_times(self, expected_call_times: int):
+ lines = self._read_contents_from_output_file()
+ assert len(lines) == expected_call_times, (
+ f"Expect to call {expected_call_times} times, but actually called"
+ f" {len(lines)} times."
+ )
+
+ def assert_called_with_args(self, expected_args: str):
+ lines = self._read_contents_from_output_file()
+ assert len(lines) > 0
+ assert re.search(expected_args, lines[0]), (
+ f"Expect to call with args {expected_args}, but actually called with"
+ f" args {lines[0]}."
+ )
+
+ def assert_not_called(self):
+ self.assert_called_with_times(0)
+
+ def assert_called_once_with_args(self, expected_args: str):
+ self.assert_called_with_times(1)
+ self.assert_called_with_args(expected_args)
+
+ def _read_contents_from_output_file(self) -> list[str]:
+ with open(self.output_file, "r") as f:
+ return f.readlines()
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/ui/build/config.go b/ui/build/config.go
index 7426a78..feded1c 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -1164,14 +1164,6 @@
c.sourceRootDirs = i
}
-func (c *configImpl) GetIncludeTags() []string {
- return c.includeTags
-}
-
-func (c *configImpl) SetIncludeTags(i []string) {
- c.includeTags = i
-}
-
func (c *configImpl) GetLogsPrefix() string {
return c.logsPrefix
}
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index e17bd54..eba86a0 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -147,7 +147,6 @@
var BannerVars = []string{
"PLATFORM_VERSION_CODENAME",
"PLATFORM_VERSION",
- "PRODUCT_INCLUDE_TAGS",
"PRODUCT_SOURCE_ROOT_DIRS",
"TARGET_PRODUCT",
"TARGET_BUILD_VARIANT",
@@ -301,6 +300,5 @@
config.SetBuildBrokenDupRules(makeVars["BUILD_BROKEN_DUP_RULES"] == "true")
config.SetBuildBrokenUsesNetwork(makeVars["BUILD_BROKEN_USES_NETWORK"] == "true")
config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(makeVars["BUILD_BROKEN_NINJA_USES_ENV_VARS"]))
- config.SetIncludeTags(strings.Fields(makeVars["PRODUCT_INCLUDE_TAGS"]))
config.SetSourceRootDirs(strings.Fields(makeVars["PRODUCT_SOURCE_ROOT_DIRS"]))
}
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 9955b1f..2f3150d 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -401,7 +401,6 @@
}
blueprintCtx := blueprint.NewContext()
- blueprintCtx.AddIncludeTags(config.GetIncludeTags()...)
blueprintCtx.AddSourceRootDirs(config.GetSourceRootDirs()...)
blueprintCtx.SetIgnoreUnknownModuleTypes(true)
blueprintConfig := BlueprintConfig{
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 3095139..24ad082 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -79,6 +79,10 @@
// bpglob is built explicitly using Microfactory
bpglob := filepath.Join(config.SoongOutDir(), "bpglob")
+ // release-config files are generated from the initial lunch or Kati phase
+ // before running soong and ninja.
+ releaseConfigDir := filepath.Join(outDir, "soong", "release-config")
+
danglingRules := make(map[string]bool)
scanner := bufio.NewScanner(stdout)
@@ -93,7 +97,8 @@
line == variablesFilePath ||
line == dexpreoptConfigFilePath ||
line == buildDatetimeFilePath ||
- line == bpglob {
+ line == bpglob ||
+ strings.HasPrefix(line, releaseConfigDir) {
// Leaf node is in one of Soong's bootstrap directories, which do not have
// full build rules in the primary build.ninja file.
continue