Merge "Assert android.ApexModule interface for types having ApexModuleBase"
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index d810726..81ca475 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -26,9 +26,10 @@
"strings"
"sync"
+ "github.com/google/blueprint/bootstrap"
+
"android/soong/bazel"
"android/soong/shared"
- "github.com/google/blueprint/bootstrap"
)
type CqueryRequestType int
@@ -60,6 +61,12 @@
// Returns true if bazel is enabled for the given configuration.
BazelEnabled() bool
+
+ // Returns the bazel output base (the root directory for all bazel intermediate outputs).
+ OutputBase() string
+
+ // Returns build statements which should get registered to reflect Bazel's outputs.
+ BuildStatementsToRegister() []bazel.BuildStatement
}
// A context object which tracks queued requests that need to be made to Bazel,
@@ -76,6 +83,9 @@
requestMutex sync.Mutex // requests can be written in parallel
results map[cqueryKey]string // Results of cquery requests after Bazel invocations
+
+ // Build statements which should get registered to reflect Bazel's outputs.
+ buildStatements []bazel.BuildStatement
}
var _ BazelContext = &bazelContext{}
@@ -103,6 +113,14 @@
return true
}
+func (m MockBazelContext) OutputBase() string {
+ return "outputbase"
+}
+
+func (m MockBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+ return []bazel.BuildStatement{}
+}
+
var _ BazelContext = MockBazelContext{}
func (bazelCtx *bazelContext) GetAllFiles(label string) ([]string, bool) {
@@ -123,10 +141,18 @@
panic("unimplemented")
}
+func (m noopBazelContext) OutputBase() string {
+ return ""
+}
+
func (n noopBazelContext) BazelEnabled() bool {
return false
}
+func (m noopBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+ return []bazel.BuildStatement{}
+}
+
func NewBazelContext(c *config) (BazelContext, error) {
// TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
// are production ready.
@@ -241,14 +267,32 @@
func (context *bazelContext) mainBzlFileContents() []byte {
contents := `
+#####################################################
# This file is generated by soong_build. Do not edit.
+#####################################################
+
def _mixed_build_root_impl(ctx):
return [DefaultInfo(files = depset(ctx.files.deps))]
+# Rule representing the root of the build, to depend on all Bazel targets that
+# are required for the build. Building this target will build the entire Bazel
+# build tree.
mixed_build_root = rule(
implementation = _mixed_build_root_impl,
attrs = {"deps" : attr.label_list()},
)
+
+def _phony_root_impl(ctx):
+ return []
+
+# Rule to depend on other targets but build nothing.
+# This is useful as follows: building a target of this rule will generate
+# symlink forests for all dependencies of the target, without executing any
+# actions of the build.
+phony_root = rule(
+ implementation = _phony_root_impl,
+ attrs = {"deps" : attr.label_list()},
+)
`
return []byte(contents)
}
@@ -268,11 +312,15 @@
func (context *bazelContext) mainBuildFileContents() []byte {
formatString := `
# This file is generated by soong_build. Do not edit.
-load(":main.bzl", "mixed_build_root")
+load(":main.bzl", "mixed_build_root", "phony_root")
mixed_build_root(name = "buildroot",
deps = [%s],
)
+
+phony_root(name = "phonyroot",
+ deps = [":buildroot"],
+)
`
var buildRootDeps []string = nil
for val, _ := range context.requests {
@@ -379,22 +427,46 @@
}
}
- // Issue a build command.
- // TODO(cparsons): Invoking bazel execution during soong_build should be avoided;
- // bazel actions should either be added to the Ninja file and executed later,
- // or bazel should handle execution.
+ // Issue an aquery command to retrieve action information about the bazel build tree.
+ //
// TODO(cparsons): Use --target_pattern_file to avoid command line limits.
- _, err = context.issueBazelCommand(bazel.BazelBuildPhonyRootRunName, "build", []string{buildroot_label})
+ var aqueryOutput string
+ aqueryOutput, err = context.issueBazelCommand(bazel.AqueryBuildRootRunName, "aquery",
+ []string{fmt.Sprintf("deps(%s)", buildroot_label),
+ // Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
+ // proto sources, which would add a number of unnecessary dependencies.
+ "--output=jsonproto"})
if err != nil {
return err
}
+ context.buildStatements = bazel.AqueryBuildStatements([]byte(aqueryOutput))
+
+ // Issue a build command of the phony root to generate symlink forests for dependencies of the
+ // Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
+ // but some of symlinks may be required to resolve source dependencies of the build.
+ _, err = context.issueBazelCommand(bazel.BazelBuildPhonyRootRunName, "build",
+ []string{"//:phonyroot"})
+
+ if err != nil {
+ return err
+ }
+
+ fmt.Printf("Build statements %s", context.buildStatements)
// Clear requests.
context.requests = map[cqueryKey]bool{}
return nil
}
+func (context *bazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+ return context.buildStatements
+}
+
+func (context *bazelContext) OutputBase() string {
+ return context.outputBase
+}
+
// Singleton used for registering BUILD file ninja dependencies (needed
// for correctness of builds which use Bazel.
func BazelSingleton() Singleton {
@@ -404,18 +476,45 @@
type bazelSingleton struct{}
func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) {
- if ctx.Config().BazelContext.BazelEnabled() {
- bazelBuildList := absolutePath(filepath.Join(
- filepath.Dir(bootstrap.ModuleListFile), "bazel.list"))
- ctx.AddNinjaFileDeps(bazelBuildList)
+ // bazelSingleton is a no-op if mixed-soong-bazel-builds are disabled.
+ if !ctx.Config().BazelContext.BazelEnabled() {
+ return
+ }
- data, err := ioutil.ReadFile(bazelBuildList)
- if err != nil {
- ctx.Errorf(err.Error())
+ // Add ninja file dependencies for files which all bazel invocations require.
+ bazelBuildList := absolutePath(filepath.Join(
+ filepath.Dir(bootstrap.ModuleListFile), "bazel.list"))
+ ctx.AddNinjaFileDeps(bazelBuildList)
+
+ data, err := ioutil.ReadFile(bazelBuildList)
+ if err != nil {
+ ctx.Errorf(err.Error())
+ }
+ files := strings.Split(strings.TrimSpace(string(data)), "\n")
+ for _, file := range files {
+ ctx.AddNinjaFileDeps(file)
+ }
+
+ // Register bazel-owned build statements (obtained from the aquery invocation).
+ for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
+ rule := NewRuleBuilder(pctx, ctx)
+ cmd := rule.Command()
+ cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && %s",
+ ctx.Config().BazelContext.OutputBase(), buildStatement.Command))
+
+ for _, outputPath := range buildStatement.OutputPaths {
+ cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
}
- files := strings.Split(strings.TrimSpace(string(data)), "\n")
- for _, file := range files {
- ctx.AddNinjaFileDeps(file)
+ for _, inputPath := range buildStatement.InputPaths {
+ cmd.Implicit(PathForBazelOut(ctx, inputPath))
}
+
+ // This is required to silence warnings pertaining to unexpected timestamps. Particularly,
+ // some Bazel builtins (such as files in the bazel_tools directory) have far-future
+ // timestamps. Without restat, Ninja would emit warnings that the input files of a
+ // build statement have later timestamps than the outputs.
+ rule.Restat()
+
+ rule.Build(fmt.Sprintf("bazel %s", index), buildStatement.Mnemonic)
}
}
diff --git a/android/config.go b/android/config.go
index 453e074..89467d8 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1324,10 +1324,6 @@
return c.productVariables.ProductPrivateSepolicyDirs
}
-func (c *config) ProductCompatibleProperty() bool {
- return Bool(c.productVariables.ProductCompatibleProperty)
-}
-
func (c *config) MissingUsesLibraries() []string {
return c.productVariables.MissingUsesLibraries
}
diff --git a/android/paths.go b/android/paths.go
index 0238a3f..10d8d0d 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1154,6 +1154,17 @@
return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
}
+type BazelOutPath struct {
+ OutputPath
+}
+
+var _ Path = BazelOutPath{}
+var _ objPathProvider = BazelOutPath{}
+
+func (p BazelOutPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+ return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
+}
+
// PathForVndkRefAbiDump returns an OptionalPath representing the path of the
// reference abi dump for the given module. This is not guaranteed to be valid.
func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string,
@@ -1192,6 +1203,24 @@
fileName+ext)
}
+// PathForBazelOut returns a Path representing the paths... under an output directory dedicated to
+// bazel-owned outputs.
+func PathForBazelOut(ctx PathContext, paths ...string) BazelOutPath {
+ execRootPathComponents := append([]string{"execroot", "__main__"}, paths...)
+ execRootPath := filepath.Join(execRootPathComponents...)
+ validatedExecRootPath, err := validatePath(execRootPath)
+ if err != nil {
+ reportPathError(ctx, err)
+ }
+
+ outputPath := OutputPath{basePath{"", ctx.Config(), ""},
+ ctx.Config().BazelContext.OutputBase()}
+
+ return BazelOutPath{
+ OutputPath: outputPath.withRel(validatedExecRootPath),
+ }
+}
+
// PathForModuleOut returns a Path representing the paths... under the module's
// output directory.
func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
diff --git a/android/variable.go b/android/variable.go
index aed145c..93dac3d 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -338,7 +338,6 @@
ProductPublicSepolicyDirs []string `json:",omitempty"`
ProductPrivateSepolicyDirs []string `json:",omitempty"`
- ProductCompatibleProperty *bool `json:",omitempty"`
ProductVndkVersion *string `json:",omitempty"`
diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt
index 0239166..c5f2bf8 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -209,6 +209,7 @@
libadbd(minSdkVersion:(no version))
libadbd_core(minSdkVersion:(no version))
libadbd_services(minSdkVersion:(no version))
+liballoc.rust_sysroot(minSdkVersion:29)
libamrextractor(minSdkVersion:29)
libapp_processes_protos_lite(minSdkVersion:(no version))
libarect(minSdkVersion:29)
@@ -223,6 +224,8 @@
libavcenc(minSdkVersion:29)
libavservices_minijail(minSdkVersion:29)
libbacktrace_headers(minSdkVersion:apex_inherit)
+libbacktrace_rs.rust_sysroot(minSdkVersion:29)
+libbacktrace_sys.rust_sysroot(minSdkVersion:29)
libbase(minSdkVersion:29)
libbase_headers(minSdkVersion:29)
libbinder_headers(minSdkVersion:29)
@@ -238,6 +241,8 @@
libc_headers(minSdkVersion:apex_inherit)
libc_headers_arch(minSdkVersion:apex_inherit)
libcap(minSdkVersion:29)
+libcfg_if(minSdkVersion:29)
+libcfg_if.rust_sysroot(minSdkVersion:29)
libclang_rt.hwasan-aarch64-android.llndk(minSdkVersion:(no version))
libcodec2(minSdkVersion:29)
libcodec2_headers(minSdkVersion:29)
@@ -276,6 +281,8 @@
libcodec2_soft_vp9dec(minSdkVersion:29)
libcodec2_soft_vp9enc(minSdkVersion:29)
libcodec2_vndk(minSdkVersion:29)
+libcompiler_builtins.rust_sysroot(minSdkVersion:29)
+libcore.rust_sysroot(minSdkVersion:29)
libcrypto(minSdkVersion:29)
libcrypto_static(minSdkVersion:(no version))
libcrypto_utils(minSdkVersion:(no version))
@@ -295,7 +302,9 @@
libfmq-base(minSdkVersion:29)
libFraunhoferAAC(minSdkVersion:29)
libgav1(minSdkVersion:29)
+libgcc(minSdkVersion:(no version))
libgcc_stripped(minSdkVersion:(no version))
+libgetopts(minSdkVersion:29)
libgralloctypes(minSdkVersion:29)
libgrallocusage(minSdkVersion:29)
libgsm(minSdkVersion:apex_inherit)
@@ -304,6 +313,7 @@
libgui_headers(minSdkVersion:29)
libhardware(minSdkVersion:29)
libhardware_headers(minSdkVersion:29)
+libhashbrown.rust_sysroot(minSdkVersion:29)
libhevcdec(minSdkVersion:29)
libhevcenc(minSdkVersion:29)
libhidlbase(minSdkVersion:29)
@@ -313,9 +323,14 @@
libion(minSdkVersion:29)
libjavacrypto(minSdkVersion:29)
libjsoncpp(minSdkVersion:29)
+liblazy_static(minSdkVersion:29)
+liblibc(minSdkVersion:29)
+liblibc.rust_sysroot(minSdkVersion:29)
libLibGuiProperties(minSdkVersion:29)
+liblibm(minSdkVersion:29)
liblog(minSdkVersion:(no version))
liblog_headers(minSdkVersion:29)
+liblog_rust(minSdkVersion:29)
liblua(minSdkVersion:(no version))
liblz4(minSdkVersion:(no version))
libm(minSdkVersion:(no version))
@@ -352,18 +367,26 @@
libneuralnetworks_common(minSdkVersion:(no version))
libneuralnetworks_headers(minSdkVersion:(no version))
liboggextractor(minSdkVersion:29)
+libonce_cell(minSdkVersion:29)
libopus(minSdkVersion:29)
+libpanic_unwind.rust_sysroot(minSdkVersion:29)
libprocessgroup(minSdkVersion:29)
libprocessgroup_headers(minSdkVersion:29)
libprocpartition(minSdkVersion:(no version))
+libprofiler_builtins.rust_sysroot(minSdkVersion:29)
libprotobuf-cpp-lite(minSdkVersion:29)
libprotobuf-java-lite(minSdkVersion:current)
libprotobuf-java-nano(minSdkVersion:9)
libprotoutil(minSdkVersion:(no version))
libqemu_pipe(minSdkVersion:(no version))
+libquiche_ffi(minSdkVersion:29)
+libring(minSdkVersion:29)
+libring-core(minSdkVersion:29)
+librustc_demangle.rust_sysroot(minSdkVersion:29)
libsfplugin_ccodec_utils(minSdkVersion:29)
libsonivoxwithoutjet(minSdkVersion:29)
libspeexresampler(minSdkVersion:29)
+libspin(minSdkVersion:29)
libssl(minSdkVersion:29)
libstagefright_amrnb_common(minSdkVersion:29)
libstagefright_amrnbdec(minSdkVersion:29)
@@ -384,6 +407,7 @@
libstagefright_m4vh263enc(minSdkVersion:29)
libstagefright_metadatautils(minSdkVersion:29)
libstagefright_mp3dec(minSdkVersion:29)
+libstagefright_mp3dec_headers(minSdkVersion:29)
libstagefright_mpeg2extractor(minSdkVersion:29)
libstagefright_mpeg2support_nocrypto(minSdkVersion:29)
libstats_jni(minSdkVersion:(no version))
@@ -393,8 +417,11 @@
libstatspush_compat(minSdkVersion:29)
libstatssocket(minSdkVersion:(no version))
libstatssocket_headers(minSdkVersion:29)
+libstd(minSdkVersion:29)
libsystem_headers(minSdkVersion:apex_inherit)
libsysutils(minSdkVersion:apex_inherit)
+libterm(minSdkVersion:29)
+libtest(minSdkVersion:29)
libtetherutilsjni(minSdkVersion:30)
libtetherutilsjni(minSdkVersion:current)
libtextclassifier(minSdkVersion:(no version))
@@ -405,6 +432,9 @@
libtflite_static(minSdkVersion:(no version))
libui(minSdkVersion:29)
libui_headers(minSdkVersion:29)
+libunicode_width.rust_sysroot(minSdkVersion:29)
+libuntrusted(minSdkVersion:29)
+libunwind.rust_sysroot(minSdkVersion:29)
libunwind_llvm(minSdkVersion:apex_inherit)
libutf(minSdkVersion:(no version))
libutils(minSdkVersion:apex_inherit)
diff --git a/apex/apex.go b/apex/apex.go
index 261284c..88d93af 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -237,6 +237,23 @@
}
}
+type apexArchBundleProperties struct {
+ Arch struct {
+ Arm struct {
+ ApexNativeDependencies
+ }
+ Arm64 struct {
+ ApexNativeDependencies
+ }
+ X86 struct {
+ ApexNativeDependencies
+ }
+ X86_64 struct {
+ ApexNativeDependencies
+ }
+ }
+}
+
// These properties can be used in override_apex to override the corresponding properties in the
// base apex.
type overridableProperties struct {
@@ -273,6 +290,7 @@
// Properties
properties apexBundleProperties
targetProperties apexTargetBundleProperties
+ archProperties apexArchBundleProperties
overridableProperties overridableProperties
vndkProperties apexVndkProperties // only for apex_vndk modules
@@ -653,6 +671,20 @@
}
}
+ // Add native modules targeting a specific arch variant
+ switch target.Arch.ArchType {
+ case android.Arm:
+ depsList = append(depsList, a.archProperties.Arch.Arm.ApexNativeDependencies)
+ case android.Arm64:
+ depsList = append(depsList, a.archProperties.Arch.Arm64.ApexNativeDependencies)
+ case android.X86:
+ depsList = append(depsList, a.archProperties.Arch.X86.ApexNativeDependencies)
+ case android.X86_64:
+ depsList = append(depsList, a.archProperties.Arch.X86_64.ApexNativeDependencies)
+ default:
+ panic(fmt.Errorf("unsupported arch %v\n", ctx.Arch().ArchType))
+ }
+
for _, d := range depsList {
addDependenciesForNativeModules(ctx, d, target, imageVariation)
}
@@ -1682,8 +1714,15 @@
}
af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
af.transitiveDep = true
+
+ // Always track transitive dependencies for host.
+ if a.Host() {
+ filesInfo = append(filesInfo, af)
+ return true
+ }
+
abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
- if !a.Host() && !abInfo.Contents.DirectlyInApex(depName) && (cc.IsStubs() || cc.HasStubsVariants()) {
+ if !abInfo.Contents.DirectlyInApex(depName) && (cc.IsStubs() || cc.HasStubsVariants()) {
// If the dependency is a stubs lib, don't include it in this APEX,
// but make sure that the lib is installed on the device.
// In case no APEX is having the lib, the lib is installed to the system
@@ -1716,7 +1755,8 @@
// else-if clause for the indirect dependencies.
// Currently, that's impossible because we would
// like to record requiredNativeLibs even when
- // DepIsInSameAPex is false.
+ // DepIsInSameAPex is false. We also shouldn't do
+ // this for host.
if !am.DepIsInSameApex(ctx, am) {
return false
}
@@ -1923,6 +1963,7 @@
module.AddProperties(&module.properties)
module.AddProperties(&module.targetProperties)
+ module.AddProperties(&module.archProperties)
module.AddProperties(&module.overridableProperties)
android.InitAndroidMultiTargetsArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index f71e7ef..69b1dbb 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3891,6 +3891,64 @@
ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared")
}
+func TestApexWithArch(t *testing.T) {
+ ctx, _ := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ arch: {
+ arm64: {
+ native_shared_libs: ["mylib.arm64"],
+ },
+ x86_64: {
+ native_shared_libs: ["mylib.x64"],
+ },
+ }
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib.arm64",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ // TODO: remove //apex_available:platform
+ apex_available: [
+ "//apex_available:platform",
+ "myapex",
+ ],
+ }
+
+ cc_library {
+ name: "mylib.x64",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ // TODO: remove //apex_available:platform
+ apex_available: [
+ "//apex_available:platform",
+ "myapex",
+ ],
+ }
+ `)
+
+ apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ // Ensure that apex variant is created for the direct dep
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib.arm64"), "android_arm64_armv8-a_shared_apex10000")
+ ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib.x64"), "android_arm64_armv8-a_shared_apex10000")
+
+ // Ensure that both direct and indirect deps are copied into apex
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.arm64.so")
+ ensureNotContains(t, copyCmds, "image.apex/lib64/mylib.x64.so")
+}
+
func TestApexWithShBinary(t *testing.T) {
ctx, _ := testApex(t, `
apex {
@@ -6559,6 +6617,11 @@
if entry.mkEntries.EntryMap["LOCAL_NOT_AVAILABLE_FOR_PLATFORM"] != nil {
t.Errorf("AndroidMk entry for \"stublib\" has LOCAL_NOT_AVAILABLE_FOR_PLATFORM set: %+v", entry.mkEntries)
}
+ cflags := entry.mkEntries.EntryMap["LOCAL_EXPORT_CFLAGS"]
+ expected := "-D__STUBLIB_API__=1"
+ if !android.InList(expected, cflags) {
+ t.Errorf("LOCAL_EXPORT_CFLAGS expected to have %q, but got %q", expected, cflags)
+ }
}
})
}
diff --git a/bazel/Android.bp b/bazel/Android.bp
index d557be5..05eddc1 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -2,10 +2,14 @@
name: "soong-bazel",
pkgPath: "android/soong/bazel",
srcs: [
+ "aquery.go",
"constants.go",
"properties.go",
],
pluginFor: [
"soong_build",
],
+ deps: [
+ "blueprint",
+ ],
}
diff --git a/bazel/aquery.go b/bazel/aquery.go
new file mode 100644
index 0000000..69d4fde
--- /dev/null
+++ b/bazel/aquery.go
@@ -0,0 +1,116 @@
+// Copyright 2020 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 bazel
+
+import (
+ "encoding/json"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+// artifact contains relevant portions of Bazel's aquery proto, Artifact.
+// Represents a single artifact, whether it's a source file or a derived output file.
+type artifact struct {
+ Id string
+ ExecPath string
+}
+
+// KeyValuePair represents Bazel's aquery proto, KeyValuePair.
+type KeyValuePair struct {
+ Key string
+ Value string
+}
+
+// depSetOfFiles contains relevant portions of Bazel's aquery proto, DepSetOfFiles.
+// Represents a data structure containing one or more files. Depsets in Bazel are an efficient
+// data structure for storing large numbers of file paths.
+type depSetOfFiles struct {
+ Id string
+ // TODO(cparsons): Handle non-flat depsets.
+ DirectArtifactIds []string
+}
+
+// action contains relevant portions of Bazel's aquery proto, Action.
+// Represents a single command line invocation in the Bazel build graph.
+type action struct {
+ Arguments []string
+ EnvironmentVariables []KeyValuePair
+ InputDepSetIds []string
+ Mnemonic string
+ OutputIds []string
+}
+
+// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
+// An aquery response from Bazel contains a single ActionGraphContainer proto.
+type actionGraphContainer struct {
+ Artifacts []artifact
+ Actions []action
+ DepSetOfFiles []depSetOfFiles
+}
+
+// BuildStatement contains information to register a build statement corresponding (one to one)
+// with a Bazel action from Bazel's action graph.
+type BuildStatement struct {
+ Command string
+ OutputPaths []string
+ InputPaths []string
+ Env []KeyValuePair
+ Mnemonic string
+}
+
+// AqueryBuildStatements returns an array of BuildStatements which should be registered (and output
+// to a ninja file) to correspond one-to-one with the given action graph json proto (from a bazel
+// aquery invocation).
+func AqueryBuildStatements(aqueryJsonProto []byte) []BuildStatement {
+ buildStatements := []BuildStatement{}
+
+ var aqueryResult actionGraphContainer
+ json.Unmarshal(aqueryJsonProto, &aqueryResult)
+
+ artifactIdToPath := map[string]string{}
+ for _, artifact := range aqueryResult.Artifacts {
+ artifactIdToPath[artifact.Id] = artifact.ExecPath
+ }
+ depsetIdToArtifactIds := map[string][]string{}
+ for _, depset := range aqueryResult.DepSetOfFiles {
+ depsetIdToArtifactIds[depset.Id] = depset.DirectArtifactIds
+ }
+
+ for _, actionEntry := range aqueryResult.Actions {
+ outputPaths := []string{}
+ for _, outputId := range actionEntry.OutputIds {
+ // TODO(cparsons): Validate the id is present.
+ outputPaths = append(outputPaths, artifactIdToPath[outputId])
+ }
+ inputPaths := []string{}
+ for _, inputDepSetId := range actionEntry.InputDepSetIds {
+ // TODO(cparsons): Validate the id is present.
+ for _, inputId := range depsetIdToArtifactIds[inputDepSetId] {
+ // TODO(cparsons): Validate the id is present.
+ inputPaths = append(inputPaths, artifactIdToPath[inputId])
+ }
+ }
+ buildStatement := BuildStatement{
+ Command: strings.Join(proptools.ShellEscapeList(actionEntry.Arguments), " "),
+ OutputPaths: outputPaths,
+ InputPaths: inputPaths,
+ Env: actionEntry.EnvironmentVariables,
+ Mnemonic: actionEntry.Mnemonic}
+ buildStatements = append(buildStatements, buildStatement)
+ }
+
+ return buildStatements
+}
diff --git a/cc/cc.go b/cc/cc.go
index 454a362..240080f 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -2411,7 +2411,14 @@
switch {
case libDepTag.header():
- // nothing
+ if !ctx.OtherModuleHasProvider(dep, HeaderLibraryInfoProvider) {
+ if !ctx.Config().AllowMissingDependencies() {
+ ctx.ModuleErrorf("module %q is not a header library", depName)
+ } else {
+ ctx.AddMissingDependencies([]string{depName})
+ }
+ return
+ }
case libDepTag.shared():
if !ctx.OtherModuleHasProvider(dep, SharedLibraryInfoProvider) {
if !ctx.Config().AllowMissingDependencies() {
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 4bcad4b..8a0d7bd 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -22,7 +22,6 @@
"android.hardware.light-ndk_platform",
"android.hardware.identity-ndk_platform",
"android.hardware.nfc@1.2",
- "android.hardware.memtrack-unstable-ndk_platform",
"android.hardware.power-ndk_platform",
"android.hardware.rebootescrow-ndk_platform",
"android.hardware.security.keymint-unstable-ndk_platform",
diff --git a/cc/library.go b/cc/library.go
index ed6755f..959d670 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -897,16 +897,22 @@
ctx.CheckbuildFile(outputFile)
- ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
- StaticLibrary: outputFile,
- ReuseObjects: library.reuseObjects,
- Objects: library.objects,
+ if library.static() {
+ ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
+ StaticLibrary: outputFile,
+ ReuseObjects: library.reuseObjects,
+ Objects: library.objects,
- TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
- Direct(outputFile).
- Transitive(deps.TranstiveStaticLibrariesForOrdering).
- Build(),
- })
+ TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
+ Direct(outputFile).
+ Transitive(deps.TranstiveStaticLibrariesForOrdering).
+ Build(),
+ })
+ }
+
+ if library.header() {
+ ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{})
+ }
return outputFile
}
@@ -1231,9 +1237,7 @@
}
// Add stub-related flags if this library is a stub library.
- if library.buildStubs() && !library.skipAPIDefine {
- library.reexportFlags("-D" + versioningMacroName(ctx.Module().(*Module).ImplementationModuleName(ctx)) + "=" + library.stubsVersion())
- }
+ library.exportVersioningMacroIfNeeded(ctx)
// Propagate a Provider containing information about exported flags, deps, and include paths.
library.flagExporter.setProvider(ctx)
@@ -1241,6 +1245,14 @@
return out
}
+func (library *libraryDecorator) exportVersioningMacroIfNeeded(ctx android.BaseModuleContext) {
+ if library.buildStubs() && !library.skipAPIDefine {
+ name := versioningMacroName(ctx.Module().(*Module).ImplementationModuleName(ctx))
+ ver := library.stubsVersion()
+ library.reexportFlags("-D" + name + "=" + ver)
+ }
+}
+
// buildStatic returns true if this library should be built as a static library.
func (library *libraryDecorator) buildStatic() bool {
return library.MutatedProperties.BuildStatic &&
diff --git a/cc/linkable.go b/cc/linkable.go
index 4efe2a7..d010985 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -130,6 +130,13 @@
var StaticLibraryInfoProvider = blueprint.NewProvider(StaticLibraryInfo{})
+// HeaderLibraryInfo is a marker provider that identifies a module as a header library.
+type HeaderLibraryInfo struct {
+}
+
+// HeaderLibraryInfoProvider is a marker provider that identifies a module as a header library.
+var HeaderLibraryInfoProvider = blueprint.NewProvider(HeaderLibraryInfo{})
+
// FlagExporterInfo is a provider to propagate transitive library information
// pertaining to exported include paths and flags.
type FlagExporterInfo struct {
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 37df4ba..2cd18cb 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -17,6 +17,7 @@
import (
"android/soong/android"
"path/filepath"
+ "strings"
)
func init() {
@@ -117,6 +118,8 @@
return nil
}
+ p.libraryDecorator.exportVersioningMacroIfNeeded(ctx)
+
in := android.PathForModuleSrc(ctx, srcs[0])
if p.static() {
@@ -190,6 +193,12 @@
}
}
+ if p.header() {
+ ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{})
+
+ return nil
+ }
+
return nil
}
@@ -220,6 +229,11 @@
p.properties.Srcs = nil
}
+// Implements versionedInterface
+func (p *prebuiltLibraryLinker) implementationModuleName(name string) string {
+ return strings.TrimPrefix(name, "prebuilt_")
+}
+
func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
module, library := NewLibrary(hod)
module.compiler = nil
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index bd1d450..74ede68 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -174,7 +174,7 @@
build.SetupOutDir(buildCtx, config)
- if config.UseBazel() {
+ if config.UseBazel() && config.Dist() {
defer populateExternalDistDir(buildCtx, config)
}
@@ -547,6 +547,12 @@
return
}
+ // Make sure the internal DIST_DIR actually exists before trying to read from it
+ if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
+ ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
+ return
+ }
+
// Make sure the external DIST_DIR actually exists before trying to write to it
if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 25b55e9..594d253 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -206,7 +206,7 @@
if ok {
var bazelOutputFiles android.Paths
for _, bazelOutputFile := range filePaths {
- bazelOutputFiles = append(bazelOutputFiles, android.PathForSource(ctx, bazelOutputFile))
+ bazelOutputFiles = append(bazelOutputFiles, android.PathForBazelOut(ctx, bazelOutputFile))
}
c.outputFiles = bazelOutputFiles
c.outputDeps = bazelOutputFiles
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 8d3cfcb..6ae9a18 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -736,7 +736,8 @@
}
gen := ctx.ModuleForTests("foo", "").Module().(*Module)
- expectedOutputFiles := []string{"bazelone.txt", "bazeltwo.txt"}
+ expectedOutputFiles := []string{"outputbase/execroot/__main__/bazelone.txt",
+ "outputbase/execroot/__main__/bazeltwo.txt"}
if !reflect.DeepEqual(gen.outputFiles.Strings(), expectedOutputFiles) {
t.Errorf("Expected output files: %q, actual: %q", expectedOutputFiles, gen.outputFiles)
}
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index df31d60..21df024 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -9,6 +9,7 @@
"device/google/cuttlefish",
"external/adhd",
"external/crosvm",
+ "external/libchromeos-rs",
"external/minijail",
"external/rust",
"external/vm_tools/p9",
diff --git a/rust/protobuf.go b/rust/protobuf.go
index 4fba34f..b91fea8 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -31,24 +31,22 @@
type PluginType int
-const (
- Protobuf PluginType = iota
- Grpc
-)
-
func init() {
android.RegisterModuleType("rust_protobuf", RustProtobufFactory)
android.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)
- android.RegisterModuleType("rust_grpcio", RustGrpcioFactory)
- android.RegisterModuleType("rust_grpcio_host", RustGrpcioHostFactory)
}
var _ SourceProvider = (*protobufDecorator)(nil)
type ProtobufProperties struct {
- // List of relative paths to proto files that will be used to generate the source
+ // List of relative paths to proto files that will be used to generate the source.
+ // Either this or grpc_protos must be defined.
Protos []string `android:"path,arch_variant"`
+ // List of relative paths to GRPC-containing proto files that will be used to generate the source.
+ // Either this or protos must be defined.
+ Grpc_protos []string `android:"path,arch_variant"`
+
// List of additional flags to pass to aprotoc
Proto_flags []string `android:"arch_variant"`
@@ -60,33 +58,54 @@
*BaseSourceProvider
Properties ProtobufProperties
- plugin PluginType
+ protoNames []string
+ grpcNames []string
+
+ grpcProtoFlags android.ProtoFlags
+ protoFlags android.ProtoFlags
}
func (proto *protobufDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path {
var protoFlags android.ProtoFlags
- var pluginPaths android.Paths
- var protoNames []string
+ var grpcProtoFlags android.ProtoFlags
+ var commonProtoFlags []string
- protoFlags.OutTypeFlag = "--rust_out"
outDir := android.PathForModuleOut(ctx)
-
- pluginPaths, protoFlags = proto.setupPlugin(ctx, protoFlags, outDir)
-
- protoFlags.Flags = append(protoFlags.Flags, defaultProtobufFlags...)
- protoFlags.Flags = append(protoFlags.Flags, proto.Properties.Proto_flags...)
-
- protoFlags.Deps = append(protoFlags.Deps, pluginPaths...)
-
protoFiles := android.PathsForModuleSrc(ctx, proto.Properties.Protos)
+ grpcFiles := android.PathsForModuleSrc(ctx, proto.Properties.Grpc_protos)
+ protoPluginPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust")
- if len(protoFiles) == 0 {
- ctx.PropertyErrorf("protos", "at least one protobuf must be defined.")
+ commonProtoFlags = append(commonProtoFlags, defaultProtobufFlags...)
+ commonProtoFlags = append(commonProtoFlags, proto.Properties.Proto_flags...)
+ commonProtoFlags = append(commonProtoFlags, "--plugin=protoc-gen-rust="+protoPluginPath.String())
+
+ if len(protoFiles) > 0 {
+ protoFlags.OutTypeFlag = "--rust_out"
+ protoFlags.Flags = append(protoFlags.Flags, commonProtoFlags...)
+
+ protoFlags.Deps = append(protoFlags.Deps, protoPluginPath)
+ }
+
+ if len(grpcFiles) > 0 {
+ grpcPath := ctx.Config().HostToolPath(ctx, "grpc_rust_plugin")
+
+ grpcProtoFlags.OutTypeFlag = "--rust_out"
+ grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, "--grpc_out="+outDir.String())
+ grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, "--plugin=protoc-gen-grpc="+grpcPath.String())
+ grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, commonProtoFlags...)
+
+ grpcProtoFlags.Deps = append(grpcProtoFlags.Deps, grpcPath, protoPluginPath)
+ }
+
+ if len(protoFiles) == 0 && len(grpcFiles) == 0 {
+ ctx.PropertyErrorf("protos",
+ "at least one protobuf must be defined in either protos or grpc_protos.")
}
// Add exported dependency include paths
for _, include := range deps.depIncludePaths {
protoFlags.Flags = append(protoFlags.Flags, "-I"+include.String())
+ grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, "-I"+include.String())
}
stem := proto.BaseSourceProvider.getStem(ctx)
@@ -98,25 +117,50 @@
var outputs android.WritablePaths
rule := android.NewRuleBuilder(pctx, ctx)
+
for _, protoFile := range protoFiles {
- protoName := strings.TrimSuffix(protoFile.Base(), ".proto")
- protoNames = append(protoNames, protoName)
-
- protoOut := android.PathForModuleOut(ctx, protoName+".rs")
- ruleOutputs := android.WritablePaths{android.WritablePath(protoOut)}
-
- if proto.plugin == Grpc {
- grpcOut := android.PathForModuleOut(ctx, protoName+grpcSuffix+".rs")
- ruleOutputs = append(ruleOutputs, android.WritablePath(grpcOut))
+ // Since we're iterating over the protoFiles already, make sure they're not redeclared in grpcFiles
+ if android.InList(protoFile.String(), grpcFiles.Strings()) {
+ ctx.PropertyErrorf("protos",
+ "A proto can only be added once to either grpc_protos or protos. %q is declared in both properties",
+ protoFile.String())
}
+ protoName := strings.TrimSuffix(protoFile.Base(), ".proto")
+ proto.protoNames = append(proto.protoNames, protoName)
+
+ protoOut := android.PathForModuleOut(ctx, protoName+".rs")
depFile := android.PathForModuleOut(ctx, protoName+".d")
+ ruleOutputs := android.WritablePaths{protoOut, depFile}
+
android.ProtoRule(rule, protoFile, protoFlags, protoFlags.Deps, outDir, depFile, ruleOutputs)
outputs = append(outputs, ruleOutputs...)
}
- android.WriteFileRule(ctx, stemFile, proto.genModFileContents(ctx, protoNames))
+ for _, grpcFile := range grpcFiles {
+ grpcName := strings.TrimSuffix(grpcFile.Base(), ".proto")
+ proto.grpcNames = append(proto.grpcNames, grpcName)
+
+ // GRPC protos produce two files, a proto.rs and a proto_grpc.rs
+ protoOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+".rs"))
+ grpcOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+grpcSuffix+".rs"))
+ depFile := android.PathForModuleOut(ctx, grpcName+".d")
+
+ ruleOutputs := android.WritablePaths{protoOut, grpcOut, depFile}
+
+ android.ProtoRule(rule, grpcFile, grpcProtoFlags, grpcProtoFlags.Deps, outDir, depFile, ruleOutputs)
+ outputs = append(outputs, ruleOutputs...)
+ }
+
+ // Check that all proto base filenames are unique as outputs are written to the same directory.
+ baseFilenames := append(proto.protoNames, proto.grpcNames...)
+ if len(baseFilenames) != len(android.FirstUniqueStrings(baseFilenames)) {
+ ctx.PropertyErrorf("protos", "proto filenames must be unique across 'protos' and 'grpc_protos' "+
+ "to be used in the same rust_protobuf module. For example, foo.proto and src/foo.proto will conflict.")
+ }
+
+ android.WriteFileRule(ctx, stemFile, proto.genModFileContents())
rule.Build("protoc_"+ctx.ModuleName(), "protoc "+ctx.ModuleName())
@@ -127,19 +171,19 @@
return stemFile
}
-func (proto *protobufDecorator) genModFileContents(ctx ModuleContext, protoNames []string) string {
+func (proto *protobufDecorator) genModFileContents() string {
lines := []string{
"// @Soong generated Source",
}
- for _, protoName := range protoNames {
+ for _, protoName := range proto.protoNames {
lines = append(lines, fmt.Sprintf("pub mod %s;", protoName))
-
- if proto.plugin == Grpc {
- lines = append(lines, fmt.Sprintf("pub mod %s%s;", protoName, grpcSuffix))
- }
}
- if proto.plugin == Grpc {
+ for _, grpcName := range proto.grpcNames {
+ lines = append(lines, fmt.Sprintf("pub mod %s;", grpcName))
+ lines = append(lines, fmt.Sprintf("pub mod %s%s;", grpcName, grpcSuffix))
+ }
+ if len(proto.grpcNames) > 0 {
lines = append(
lines,
"pub mod empty {",
@@ -150,27 +194,6 @@
return strings.Join(lines, "\n")
}
-func (proto *protobufDecorator) setupPlugin(ctx ModuleContext, protoFlags android.ProtoFlags, outDir android.ModuleOutPath) (android.Paths, android.ProtoFlags) {
- pluginPaths := []android.Path{}
-
- if proto.plugin == Protobuf {
- pluginPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust")
- pluginPaths = append(pluginPaths, pluginPath)
- protoFlags.Flags = append(protoFlags.Flags, "--plugin="+pluginPath.String())
- } else if proto.plugin == Grpc {
- grpcPath := ctx.Config().HostToolPath(ctx, "grpc_rust_plugin")
- protobufPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust")
- pluginPaths = append(pluginPaths, grpcPath, protobufPath)
- protoFlags.Flags = append(protoFlags.Flags, "--grpc_out="+outDir.String())
- protoFlags.Flags = append(protoFlags.Flags, "--plugin=protoc-gen-grpc="+grpcPath.String())
- protoFlags.Flags = append(protoFlags.Flags, "--plugin=protoc-gen-rust="+protobufPath.String())
- } else {
- ctx.ModuleErrorf("Unknown protobuf plugin type requested")
- }
-
- return pluginPaths, protoFlags
-}
-
func (proto *protobufDecorator) SourceProviderProps() []interface{} {
return append(proto.BaseSourceProvider.SourceProviderProps(), &proto.Properties)
}
@@ -180,7 +203,7 @@
deps.Rustlibs = append(deps.Rustlibs, "libprotobuf")
deps.HeaderLibs = append(deps.SharedLibs, proto.Properties.Header_libs...)
- if proto.plugin == Grpc {
+ if len(proto.Properties.Grpc_protos) > 0 {
deps.Rustlibs = append(deps.Rustlibs, "libgrpcio", "libfutures")
deps.HeaderLibs = append(deps.HeaderLibs, "libprotobuf-cpp-full")
}
@@ -203,34 +226,10 @@
return module.Init()
}
-func RustGrpcioFactory() android.Module {
- module, _ := NewRustGrpcio(android.HostAndDeviceSupported)
- return module.Init()
-}
-
-// A host-only variant of rust_protobuf. Refer to rust_protobuf for more details.
-func RustGrpcioHostFactory() android.Module {
- module, _ := NewRustGrpcio(android.HostSupported)
- return module.Init()
-}
-
func NewRustProtobuf(hod android.HostOrDeviceSupported) (*Module, *protobufDecorator) {
protobuf := &protobufDecorator{
BaseSourceProvider: NewSourceProvider(),
Properties: ProtobufProperties{},
- plugin: Protobuf,
- }
-
- module := NewSourceProviderModule(hod, protobuf, false)
-
- return module, protobuf
-}
-
-func NewRustGrpcio(hod android.HostOrDeviceSupported) (*Module, *protobufDecorator) {
- protobuf := &protobufDecorator{
- BaseSourceProvider: NewSourceProvider(),
- Properties: ProtobufProperties{},
- plugin: Grpc,
}
module := NewSourceProviderModule(hod, protobuf, false)
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index 608a4e8..1ac66f3 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -69,31 +69,19 @@
}
}
-func TestRustGrpcio(t *testing.T) {
+func TestRustGrpc(t *testing.T) {
ctx := testRust(t, `
- rust_grpcio {
+ rust_protobuf {
name: "librust_grpcio",
- protos: ["buf.proto", "proto.proto"],
+ protos: ["buf.proto"],
+ grpc_protos: ["foo.proto", "proto.proto"],
crate_name: "rust_grpcio",
source_stem: "buf",
- shared_libs: ["libfoo_shared"],
- static_libs: ["libfoo_static"],
- }
- cc_library_shared {
- name: "libfoo_shared",
- export_include_dirs: ["shared_include"],
- }
- cc_library_static {
- name: "libfoo_static",
- export_include_dirs: ["static_include"],
}
`)
// Check that libprotobuf is added as a dependency.
librust_grpcio_module := ctx.ModuleForTests("librust_grpcio", "android_arm64_armv8-a_dylib").Module().(*Module)
- if !android.InList("libprotobuf", librust_grpcio_module.Properties.AndroidMkDylibs) {
- t.Errorf("libprotobuf dependency missing for rust_grpcio (dependency missing from AndroidMkDylibs)")
- }
// Check that libgrpcio is added as a dependency.
if !android.InList("libgrpcio", librust_grpcio_module.Properties.AndroidMkDylibs) {
@@ -106,20 +94,12 @@
}
// Make sure the correct plugin is being used.
- librust_grpcio_out := ctx.ModuleForTests("librust_grpcio", "android_arm64_armv8-a_source").Output("buf_grpc.rs")
+ librust_grpcio_out := ctx.ModuleForTests("librust_grpcio", "android_arm64_armv8-a_source").Output("foo_grpc.rs")
cmd := librust_grpcio_out.RuleParams.Command
if w := "protoc-gen-grpc"; !strings.Contains(cmd, w) {
t.Errorf("expected %q in %q", w, cmd)
}
- // Check exported include directories
- if w := "-Ishared_include"; !strings.Contains(cmd, w) {
- t.Errorf("expected %q in %q", w, cmd)
- }
- if w := "-Istatic_include"; !strings.Contains(cmd, w) {
- t.Errorf("expected %q in %q", w, cmd)
- }
-
// Check that we're including the exported directory from libprotobuf-cpp-full
if w := "-Ilibprotobuf-cpp-full-includes"; !strings.Contains(cmd, w) {
t.Errorf("expected %q in %q", w, cmd)
@@ -132,3 +112,25 @@
librust_grpcio_outputs)
}
}
+
+func TestRustProtoErrors(t *testing.T) {
+ testRustError(t, "A proto can only be added once to either grpc_protos or protos.*", `
+ rust_protobuf {
+ name: "librust_grpcio",
+ protos: ["buf.proto"],
+ grpc_protos: ["buf.proto"],
+ crate_name: "rust_grpcio",
+ source_stem: "buf",
+ }
+ `)
+
+ testRustError(t, "proto filenames must be unique across 'protos' and 'grpc_protos'.*", `
+ rust_protobuf {
+ name: "librust_grpcio",
+ protos: ["buf.proto"],
+ grpc_protos: ["proto/buf.proto"],
+ crate_name: "rust_grpcio",
+ source_stem: "buf",
+ }
+ `)
+}
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 4edc6cd..48c8d74 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -106,14 +106,16 @@
// useMockedFs setup a default mocked filesystem for the test environment.
func (tctx *testRustCtx) useMockedFs() {
tctx.fs = map[string][]byte{
- "foo.rs": nil,
- "foo.c": nil,
- "src/bar.rs": nil,
- "src/any.h": nil,
- "proto.proto": nil,
- "buf.proto": nil,
- "liby.so": nil,
- "libz.so": nil,
+ "foo.rs": nil,
+ "foo.c": nil,
+ "src/bar.rs": nil,
+ "src/any.h": nil,
+ "proto.proto": nil,
+ "proto/buf.proto": nil,
+ "buf.proto": nil,
+ "foo.proto": nil,
+ "liby.so": nil,
+ "libz.so": nil,
}
}
diff --git a/rust/testing.go b/rust/testing.go
index 963a4ea..07f557a 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -153,8 +153,6 @@
ctx.RegisterModuleType("rust_ffi_host", RustFFIHostFactory)
ctx.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory)
- ctx.RegisterModuleType("rust_grpcio", RustGrpcioFactory)
- ctx.RegisterModuleType("rust_grpcio_host", RustGrpcioHostFactory)
ctx.RegisterModuleType("rust_proc_macro", ProcMacroFactory)
ctx.RegisterModuleType("rust_protobuf", RustProtobufFactory)
ctx.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)
diff --git a/ui/metrics/event.go b/ui/metrics/event.go
index 6becfd1..87c1b84 100644
--- a/ui/metrics/event.go
+++ b/ui/metrics/event.go
@@ -14,6 +14,17 @@
package metrics
+// This file contains the functionality to represent a build event in respect
+// to the metric system. A build event corresponds to a block of scoped code
+// that contains a "Begin()" and immediately followed by "defer End()" trace.
+// When defined, the duration of the scoped code is measure along with other
+// performance measurements such as memory.
+//
+// As explained in the metrics package, the metrics system is a stacked based
+// system since the collected metrics is considered to be topline metrics.
+// The steps of the build system in the UI layer is sequential. Hence, the
+// functionality defined below follows the stack data structure operations.
+
import (
"os"
"syscall"
@@ -21,54 +32,97 @@
"android/soong/ui/metrics/metrics_proto"
"android/soong/ui/tracer"
+
"github.com/golang/protobuf/proto"
)
-// for testing purpose only
-var _now = now
-
-type event struct {
- desc string
- name string
-
- // the time that the event started to occur.
- start time.Time
-
- // The list of process resource information that was executed
- procResInfo []*soong_metrics_proto.ProcessResourceInfo
-}
-
-type EventTracer interface {
- Begin(name, desc string, thread tracer.Thread)
- End(thread tracer.Thread) soong_metrics_proto.PerfInfo
- AddProcResInfo(string, *os.ProcessState)
-}
-
-type eventTracerImpl struct {
- activeEvents []event
-}
-
-var _ EventTracer = &eventTracerImpl{}
-
-func now() time.Time {
+// _now wraps the time.Now() function. _now is declared for unit testing purpose.
+var _now = func() time.Time {
return time.Now()
}
-// AddProcResInfo adds information on an executed process such as max resident set memory
-// and the number of voluntary context switches.
-func (t *eventTracerImpl) AddProcResInfo(name string, state *os.ProcessState) {
- if len(t.activeEvents) < 1 {
+// event holds the performance metrics data of a single build event.
+type event struct {
+ // The event name (mostly used for grouping a set of events)
+ name string
+
+ // The description of the event (used to uniquely identify an event
+ // for metrics analysis).
+ desc string
+
+ // The time that the event started to occur.
+ start time.Time
+
+ // The list of process resource information that was executed.
+ procResInfo []*soong_metrics_proto.ProcessResourceInfo
+}
+
+// newEvent returns an event with start populated with the now time.
+func newEvent(name, desc string) *event {
+ return &event{
+ name: name,
+ desc: desc,
+ start: _now(),
+ }
+}
+
+func (e event) perfInfo() soong_metrics_proto.PerfInfo {
+ realTime := uint64(_now().Sub(e.start).Nanoseconds())
+ return soong_metrics_proto.PerfInfo{
+ Desc: proto.String(e.desc),
+ Name: proto.String(e.name),
+ StartTime: proto.Uint64(uint64(e.start.UnixNano())),
+ RealTime: proto.Uint64(realTime),
+ ProcessesResourceInfo: e.procResInfo,
+ }
+}
+
+// EventTracer is an array of events that provides functionality to trace a
+// block of code on time and performance. The End call expects the Begin is
+// invoked, otherwise panic is raised.
+type EventTracer []*event
+
+// empty returns true if there are no pending events.
+func (t *EventTracer) empty() bool {
+ return len(*t) == 0
+}
+
+// lastIndex returns the index of the last element of events.
+func (t *EventTracer) lastIndex() int {
+ return len(*t) - 1
+}
+
+// peek returns the active build event.
+func (t *EventTracer) peek() *event {
+ return (*t)[t.lastIndex()]
+}
+
+// push adds the active build event in the stack.
+func (t *EventTracer) push(e *event) {
+ *t = append(*t, e)
+}
+
+// pop removes the active event from the stack since the event has completed.
+// A panic is raised if there are no pending events.
+func (t *EventTracer) pop() *event {
+ if t.empty() {
+ panic("Internal error: No pending events")
+ }
+ e := (*t)[t.lastIndex()]
+ *t = (*t)[:t.lastIndex()]
+ return e
+}
+
+// AddProcResInfo adds information on an executed process such as max resident
+// set memory and the number of voluntary context switches.
+func (t *EventTracer) AddProcResInfo(name string, state *os.ProcessState) {
+ if t.empty() {
return
}
rusage := state.SysUsage().(*syscall.Rusage)
- // The implementation of the metrics system is a stacked based system. The steps of the
- // build system in the UI layer is sequential so the Begin function is invoked when a
- // function (or scoped code) is invoked. That is translated to a new event which is added
- // at the end of the activeEvents array. When the invoking function is completed, End is
- // invoked which is a pop operation from activeEvents.
- curEvent := &t.activeEvents[len(t.activeEvents)-1]
- curEvent.procResInfo = append(curEvent.procResInfo, &soong_metrics_proto.ProcessResourceInfo{
+ e := t.peek()
+ e.procResInfo = append(e.procResInfo, &soong_metrics_proto.ProcessResourceInfo{
Name: proto.String(name),
UserTimeMicros: proto.Uint64(uint64(rusage.Utime.Usec)),
SystemTimeMicros: proto.Uint64(uint64(rusage.Stime.Usec)),
@@ -82,23 +136,13 @@
})
}
-func (t *eventTracerImpl) Begin(name, desc string, _ tracer.Thread) {
- t.activeEvents = append(t.activeEvents, event{name: name, desc: desc, start: _now()})
+// Begin starts tracing the event.
+func (t *EventTracer) Begin(name, desc string, _ tracer.Thread) {
+ t.push(newEvent(name, desc))
}
-func (t *eventTracerImpl) End(tracer.Thread) soong_metrics_proto.PerfInfo {
- if len(t.activeEvents) < 1 {
- panic("Internal error: No pending events for endAt to end!")
- }
- lastEvent := t.activeEvents[len(t.activeEvents)-1]
- t.activeEvents = t.activeEvents[:len(t.activeEvents)-1]
- realTime := uint64(_now().Sub(lastEvent.start).Nanoseconds())
-
- return soong_metrics_proto.PerfInfo{
- Desc: proto.String(lastEvent.desc),
- Name: proto.String(lastEvent.name),
- StartTime: proto.Uint64(uint64(lastEvent.start.UnixNano())),
- RealTime: proto.Uint64(realTime),
- ProcessesResourceInfo: lastEvent.procResInfo,
- }
+// End performs post calculations such as duration of the event, aggregates
+// the collected performance information into PerfInfo protobuf message.
+func (t *EventTracer) End(tracer.Thread) soong_metrics_proto.PerfInfo {
+ return t.pop().perfInfo()
}
diff --git a/ui/metrics/event_test.go b/ui/metrics/event_test.go
index 6fc0b50..043450b 100644
--- a/ui/metrics/event_test.go
+++ b/ui/metrics/event_test.go
@@ -28,14 +28,14 @@
_now = func() time.Time { return startTime.Add(dur) }
defer func() { _now = initialNow }()
- eventTracer := &eventTracerImpl{}
- eventTracer.activeEvents = append(eventTracer.activeEvents, event{
+ et := &EventTracer{}
+ et.push(&event{
desc: "test",
name: "test",
start: startTime,
})
- perf := eventTracer.End(tracer.Thread(0))
+ perf := et.End(tracer.Thread(0))
if perf.GetRealTime() != uint64(dur.Nanoseconds()) {
t.Errorf("got %d, want %d nanoseconds for event duration", perf.GetRealTime(), dur.Nanoseconds())
}
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 7031042..efb572c 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -12,8 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// Package metrics represents the metrics system for Android Platform Build Systems.
package metrics
+// This is the main heart of the metrics system for Android Platform Build Systems.
+// The starting of the soong_ui (cmd/soong_ui/main.go), the metrics system is
+// initialized by the invocation of New and is then stored in the context
+// (ui/build/context.go) to be used throughout the system. During the build
+// initialization phase, several functions in this file are invoked to store
+// information such as the environment, build configuration and build metadata.
+// There are several scoped code that has Begin() and defer End() functions
+// that captures the metrics and is them added as a perfInfo into the set
+// of the collected metrics. Finally, when soong_ui has finished the build,
+// the defer Dump function is invoked to store the collected metrics to the
+// raw protobuf file in the $OUT directory.
+//
+// There is one additional step that occurs after the raw protobuf file is written.
+// If the configuration environment variable ANDROID_ENABLE_METRICS_UPLOAD is
+// set with the path, the raw protobuf file is uploaded to the destination. See
+// ui/build/upload.go for more details. The filename of the raw protobuf file
+// and the list of files to be uploaded is defined in cmd/soong_ui/main.go.
+//
+// See ui/metrics/event.go for the explanation of what an event is and how
+// the metrics system is a stack based system.
+
import (
"io/ioutil"
"os"
@@ -23,33 +45,51 @@
"github.com/golang/protobuf/proto"
- soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
+ "android/soong/ui/metrics/metrics_proto"
)
const (
- PrimaryNinja = "ninja"
- RunKati = "kati"
+ // Below is a list of names passed in to the Begin tracing functions. These
+ // names are used to group a set of metrics.
+
+ // Setup and tear down of the build systems.
RunSetupTool = "setup"
RunShutdownTool = "shutdown"
- RunSoong = "soong"
- RunBazel = "bazel"
TestRun = "test"
- Total = "total"
+
+ // List of build system tools.
+ RunSoong = "soong"
+ PrimaryNinja = "ninja"
+ RunKati = "kati"
+ RunBazel = "bazel"
+
+ // Overall build from building the graph to building the target.
+ Total = "total"
)
+// Metrics is a struct that stores collected metrics during the course
+// of a build which later is dumped to a MetricsBase protobuf file.
+// See ui/metrics/metrics_proto/metrics.proto for further details
+// on what information is collected.
type Metrics struct {
- metrics soong_metrics_proto.MetricsBase
- EventTracer EventTracer
+ // The protobuf message that is later written to the file.
+ metrics soong_metrics_proto.MetricsBase
+
+ // A list of pending build events.
+ EventTracer *EventTracer
}
+// New returns a pointer of Metrics to store a set of metrics.
func New() (metrics *Metrics) {
m := &Metrics{
metrics: soong_metrics_proto.MetricsBase{},
- EventTracer: &eventTracerImpl{},
+ EventTracer: &EventTracer{},
}
return m
}
+// SetTimeMetrics stores performance information from an executed block of
+// code.
func (m *Metrics) SetTimeMetrics(perf soong_metrics_proto.PerfInfo) {
switch perf.GetName() {
case RunKati:
@@ -60,19 +100,26 @@
m.metrics.BazelRuns = append(m.metrics.BazelRuns, &perf)
case PrimaryNinja:
m.metrics.NinjaRuns = append(m.metrics.NinjaRuns, &perf)
+ case RunSetupTool:
+ m.metrics.SetupTools = append(m.metrics.SetupTools, &perf)
case Total:
m.metrics.Total = &perf
}
}
+// BuildConfig stores information about the build configuration.
func (m *Metrics) BuildConfig(b *soong_metrics_proto.BuildConfig) {
m.metrics.BuildConfig = b
}
+// SystemResourceInfo stores information related to the host system such
+// as total CPU and memory.
func (m *Metrics) SystemResourceInfo(b *soong_metrics_proto.SystemResourceInfo) {
m.metrics.SystemResourceInfo = b
}
+// SetMetadataMetrics sets information about the build such as the target
+// product, host architecture and out directory.
func (m *Metrics) SetMetadataMetrics(metadata map[string]string) {
for k, v := range metadata {
switch k {
@@ -92,15 +139,15 @@
m.metrics.TargetBuildVariant = soong_metrics_proto.MetricsBase_ENG.Enum()
}
case "TARGET_ARCH":
- m.metrics.TargetArch = m.getArch(v)
+ m.metrics.TargetArch = arch(v)
case "TARGET_ARCH_VARIANT":
m.metrics.TargetArchVariant = proto.String(v)
case "TARGET_CPU_VARIANT":
m.metrics.TargetCpuVariant = proto.String(v)
case "HOST_ARCH":
- m.metrics.HostArch = m.getArch(v)
+ m.metrics.HostArch = arch(v)
case "HOST_2ND_ARCH":
- m.metrics.Host_2NdArch = m.getArch(v)
+ m.metrics.Host_2NdArch = arch(v)
case "HOST_OS_EXTRA":
m.metrics.HostOsExtra = proto.String(v)
case "HOST_CROSS_OS":
@@ -115,8 +162,10 @@
}
}
-func (m *Metrics) getArch(arch string) *soong_metrics_proto.MetricsBase_Arch {
- switch arch {
+// arch returns the corresponding MetricsBase_Arch based on the string
+// parameter.
+func arch(a string) *soong_metrics_proto.MetricsBase_Arch {
+ switch a {
case "arm":
return soong_metrics_proto.MetricsBase_ARM.Enum()
case "arm64":
@@ -130,37 +179,51 @@
}
}
+// SetBuildDateTime sets the build date and time. The value written
+// to the protobuf file is in seconds.
func (m *Metrics) SetBuildDateTime(buildTimestamp time.Time) {
m.metrics.BuildDateTimestamp = proto.Int64(buildTimestamp.UnixNano() / int64(time.Second))
}
+// SetBuildCommand adds the build command specified by the user to the
+// list of collected metrics.
func (m *Metrics) SetBuildCommand(cmd []string) {
m.metrics.BuildCommand = proto.String(strings.Join(cmd, " "))
}
-// exports the output to the file at outputPath
-func (m *Metrics) Dump(outputPath string) error {
+// Dump exports the collected metrics from the executed build to the file at
+// out path.
+func (m *Metrics) Dump(out string) error {
// ignore the error if the hostname could not be retrieved as it
// is not a critical metric to extract.
if hostname, err := os.Hostname(); err == nil {
m.metrics.Hostname = proto.String(hostname)
}
m.metrics.HostOs = proto.String(runtime.GOOS)
- return writeMessageToFile(&m.metrics, outputPath)
+
+ return save(&m.metrics, out)
}
+// SetSoongBuildMetrics sets the metrics collected from the soong_build
+// execution.
func (m *Metrics) SetSoongBuildMetrics(metrics *soong_metrics_proto.SoongBuildMetrics) {
m.metrics.SoongBuildMetrics = metrics
}
+// A CriticalUserJourneysMetrics is a struct that contains critical user journey
+// metrics. These critical user journeys are defined under cuj/cuj.go file.
type CriticalUserJourneysMetrics struct {
+ // A list of collected CUJ metrics.
cujs soong_metrics_proto.CriticalUserJourneysMetrics
}
+// NewCriticalUserJourneyMetrics returns a pointer of CriticalUserJourneyMetrics
+// to capture CUJs metrics.
func NewCriticalUserJourneysMetrics() *CriticalUserJourneysMetrics {
return &CriticalUserJourneysMetrics{}
}
+// Add adds a set of collected metrics from an executed critical user journey.
func (c *CriticalUserJourneysMetrics) Add(name string, metrics *Metrics) {
c.cujs.Cujs = append(c.cujs.Cujs, &soong_metrics_proto.CriticalUserJourneyMetrics{
Name: proto.String(name),
@@ -168,22 +231,25 @@
})
}
-func (c *CriticalUserJourneysMetrics) Dump(outputPath string) (err error) {
- return writeMessageToFile(&c.cujs, outputPath)
+// Dump saves the collected CUJs metrics to the raw protobuf file.
+func (c *CriticalUserJourneysMetrics) Dump(filename string) (err error) {
+ return save(&c.cujs, filename)
}
-func writeMessageToFile(pb proto.Message, outputPath string) (err error) {
+// save takes a protobuf message, marshals to an array of bytes
+// and is then saved to a file.
+func save(pb proto.Message, filename string) (err error) {
data, err := proto.Marshal(pb)
if err != nil {
return err
}
- tempPath := outputPath + ".tmp"
- err = ioutil.WriteFile(tempPath, []byte(data), 0644)
- if err != nil {
+
+ tempFilename := filename + ".tmp"
+ if err := ioutil.WriteFile(tempFilename, []byte(data), 0644 /* rw-r--r-- */); err != nil {
return err
}
- err = os.Rename(tempPath, outputPath)
- if err != nil {
+
+ if err := os.Rename(tempFilename, filename); err != nil {
return err
}