Merge "Remove soong_metrics_proto from the import of the protobuf file."
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/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/apex/allowed_deps.txt b/apex/allowed_deps.txt
index 3c9791f..c5f2bf8 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -407,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))
diff --git a/apex/apex.go b/apex/apex.go
index 261284c..82d7ecf 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1682,8 +1682,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 +1723,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
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index f71e7ef..506c6fe 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -6559,6 +6559,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 7a94f89..89f32f1 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/genrule/genrule.go b/genrule/genrule.go
index 93938c9..9054017 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)