Merge "rust: collect file dependencies for clippy"
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 8176299..2fc4782 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -99,22 +99,24 @@
return proptools.Bool(p.properties.Prefer)
}
-// The below source-related functions and the srcs, src fields are based on an assumption that
-// prebuilt modules have a static source property at the moment. Currently there is only one
-// exception, android_app_import, which chooses a source file depending on the product's DPI
-// preference configs. We'll want to add native support for dynamic source cases if we end up having
-// more modules like this.
-func (p *Prebuilt) SingleSourcePath(ctx ModuleContext) Path {
- if p.srcsSupplier != nil {
- srcs := p.srcsSupplier(ctx, ctx.Module())
+// SingleSourcePathFromSupplier invokes the supplied supplier for the current module in the
+// supplied context to retrieve a list of file paths, ensures that the returned list of file paths
+// contains a single value and then assumes that is a module relative file path and converts it to
+// a Path accordingly.
+//
+// Any issues, such as nil supplier or not exactly one file path will be reported as errors on the
+// supplied context and this will return nil.
+func SingleSourcePathFromSupplier(ctx ModuleContext, srcsSupplier PrebuiltSrcsSupplier, srcsPropertyName string) Path {
+ if srcsSupplier != nil {
+ srcs := srcsSupplier(ctx, ctx.Module())
if len(srcs) == 0 {
- ctx.PropertyErrorf(p.srcsPropertyName, "missing prebuilt source file")
+ ctx.PropertyErrorf(srcsPropertyName, "missing prebuilt source file")
return nil
}
if len(srcs) > 1 {
- ctx.PropertyErrorf(p.srcsPropertyName, "multiple prebuilt source files")
+ ctx.PropertyErrorf(srcsPropertyName, "multiple prebuilt source files")
return nil
}
@@ -128,6 +130,15 @@
}
}
+// The below source-related functions and the srcs, src fields are based on an assumption that
+// prebuilt modules have a static source property at the moment. Currently there is only one
+// exception, android_app_import, which chooses a source file depending on the product's DPI
+// preference configs. We'll want to add native support for dynamic source cases if we end up having
+// more modules like this.
+func (p *Prebuilt) SingleSourcePath(ctx ModuleContext) Path {
+ return SingleSourcePathFromSupplier(ctx, p.srcsSupplier, p.srcsPropertyName)
+}
+
func (p *Prebuilt) UsePrebuilt() bool {
return p.properties.UsePrebuilt
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 87d551b..c507fb0 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4316,6 +4316,14 @@
}
}
+func TestPrebuiltMissingSrc(t *testing.T) {
+ testApexError(t, `module "myapex" variant "android_common".*: prebuilt_apex does not support "arm64_armv8-a"`, `
+ prebuilt_apex {
+ name: "myapex",
+ }
+ `)
+}
+
func TestPrebuiltFilenameOverride(t *testing.T) {
ctx := testApex(t, `
prebuilt_apex {
@@ -6360,8 +6368,7 @@
}
func TestAppSetBundlePrebuilt(t *testing.T) {
- ctx := testApex(t, "", android.FixtureModifyMockFS(func(fs android.MockFS) {
- bp := `
+ bp := `
apex_set {
name: "myapex",
filename: "foo_v2.apex",
@@ -6369,24 +6376,23 @@
none: { set: "myapex.apks", },
hwaddress: { set: "myapex.hwasan.apks", },
},
- }`
- fs["Android.bp"] = []byte(bp)
- }),
- prepareForTestWithSantitizeHwaddress,
- )
+ }
+ `
+ ctx := testApex(t, bp, prepareForTestWithSantitizeHwaddress)
- m := ctx.ModuleForTests("myapex", "android_common")
- extractedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
+ // Check that the extractor produces the correct output file from the correct input file.
+ extractorOutput := "out/soong/.intermediates/myapex.apex.extractor/android_common/extracted/myapex.hwasan.apks"
- actual := extractedApex.Inputs
- if len(actual) != 1 {
- t.Errorf("expected a single input")
- }
+ m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
+ extractedApex := m.Output(extractorOutput)
- expected := "myapex.hwasan.apks"
- if actual[0].String() != expected {
- t.Errorf("expected %s, got %s", expected, actual[0].String())
- }
+ android.AssertArrayString(t, "extractor input", []string{"myapex.hwasan.apks"}, extractedApex.Inputs.Strings())
+
+ // Ditto for the apex.
+ m = ctx.ModuleForTests("myapex", "android_common")
+ copiedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
+
+ android.AssertStringEquals(t, "myapex input", extractorOutput, copiedApex.Input.String())
}
func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
@@ -7022,10 +7028,10 @@
}),
)
- m := ctx.ModuleForTests("myapex", "android_common")
+ m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
// Check extract_apks tool parameters.
- extractedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
+ extractedApex := m.Output("extracted/myapex.apks")
actual := extractedApex.Args["abis"]
expected := "ARMEABI_V7A,ARM64_V8A"
if actual != expected {
@@ -7037,6 +7043,7 @@
t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual)
}
+ m = ctx.ModuleForTests("myapex", "android_common")
a := m.Module().(*ApexSet)
expectedOverrides := []string{"foo"}
actualOverrides := android.AndroidMkEntriesForTest(t, ctx, a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go
index 574166a..aa0d9c4 100644
--- a/apex/boot_image_test.go
+++ b/apex/boot_image_test.go
@@ -305,6 +305,7 @@
`)
java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common", []string{
+ `com.android.art.apex.selector`,
`prebuilt_bar`,
`prebuilt_foo`,
})
diff --git a/apex/deapexer.go b/apex/deapexer.go
index 46ce41f..1db13f9 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -49,35 +49,31 @@
Exported_java_libs []string
}
+type SelectedApexProperties struct {
+ // The path to the apex selected for use by this module.
+ //
+ // Is tagged as `android:"path"` because it will usually contain a string of the form ":<module>"
+ // and is tagged as "`blueprint:"mutate"` because it is only initialized in a LoadHook not an
+ // Android.bp file.
+ Selected_apex *string `android:"path" blueprint:"mutated"`
+}
+
type Deapexer struct {
android.ModuleBase
- prebuilt android.Prebuilt
- properties DeapexerProperties
- apexFileProperties ApexFileProperties
+ properties DeapexerProperties
+ selectedApexProperties SelectedApexProperties
inputApex android.Path
}
func privateDeapexerFactory() android.Module {
module := &Deapexer{}
- module.AddProperties(
- &module.properties,
- &module.apexFileProperties,
- )
- android.InitPrebuiltModuleWithSrcSupplier(module, module.apexFileProperties.prebuiltApexSelector, "src")
+ module.AddProperties(&module.properties, &module.selectedApexProperties)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
-func (p *Deapexer) Prebuilt() *android.Prebuilt {
- return &p.prebuilt
-}
-
-func (p *Deapexer) Name() string {
- return p.prebuilt.Name(p.ModuleBase.Name())
-}
-
func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
// Add dependencies from the java modules to which this exports files from the `.apex` file onto
// this module so that they can access the `DeapexerInfo` object that this provides.
@@ -88,7 +84,7 @@
}
func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
+ p.inputApex = android.OptionalPathForModuleSrc(ctx, p.selectedApexProperties.Selected_apex).Path()
// Create and remember the directory into which the .apex file's contents will be unpacked.
deapexerOutput := android.PathForModuleOut(ctx, "deapexer")
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 68f2859..c8a0c0b 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -48,6 +48,9 @@
type prebuiltCommon struct {
prebuilt android.Prebuilt
properties prebuiltCommonProperties
+
+ deapexerProperties DeapexerProperties
+ selectedApexProperties SelectedApexProperties
}
type sanitizedPrebuilt interface {
@@ -91,208 +94,16 @@
return false
}
-type Prebuilt struct {
- android.ModuleBase
- prebuiltCommon
-
- properties PrebuiltProperties
-
- inputApex android.Path
- installDir android.InstallPath
- installFilename string
- outputApex android.WritablePath
-
- // list of commands to create symlinks for backward compatibility.
- // these commands will be attached as LOCAL_POST_INSTALL_CMD
- compatSymlinks []string
-}
-
-type ApexFileProperties struct {
- // the path to the prebuilt .apex file to import.
- //
- // This cannot be marked as `android:"arch_variant"` because the `prebuilt_apex` is only mutated
- // for android_common. That is so that it will have the same arch variant as, and so be compatible
- // with, the source `apex` module type that it replaces.
- Src *string
- Arch struct {
- Arm struct {
- Src *string
- }
- Arm64 struct {
- Src *string
- }
- X86 struct {
- Src *string
- }
- X86_64 struct {
- Src *string
- }
- }
-}
-
-// prebuiltApexSelector selects the correct prebuilt APEX file for the build target.
-//
-// The ctx parameter can be for any module not just the prebuilt module so care must be taken not
-// to use methods on it that are specific to the current module.
-//
-// See the ApexFileProperties.Src property.
-func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, prebuilt android.Module) []string {
- multiTargets := prebuilt.MultiTargets()
- if len(multiTargets) != 1 {
- ctx.OtherModuleErrorf(prebuilt, "compile_multilib shouldn't be \"both\" for prebuilt_apex")
- return nil
- }
- var src string
- switch multiTargets[0].Arch.ArchType {
- case android.Arm:
- src = String(p.Arch.Arm.Src)
- case android.Arm64:
- src = String(p.Arch.Arm64.Src)
- case android.X86:
- src = String(p.Arch.X86.Src)
- case android.X86_64:
- src = String(p.Arch.X86_64.Src)
- default:
- ctx.OtherModuleErrorf(prebuilt, "prebuilt_apex does not support %q", multiTargets[0].Arch.String())
- return nil
- }
- if src == "" {
- src = String(p.Src)
- }
-
- return []string{src}
-}
-
-type PrebuiltProperties struct {
- ApexFileProperties
- DeapexerProperties
-
- Installable *bool
- // Optional name for the installed apex. If unspecified, name of the
- // module is used as the file name
- Filename *string
-
- // Names of modules to be overridden. Listed modules can only be other binaries
- // (in Make or Soong).
- // This does not completely prevent installation of the overridden binaries, but if both
- // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
- // from PRODUCT_PACKAGES.
- Overrides []string
-}
-
-func (a *Prebuilt) hasSanitizedSource(sanitizer string) bool {
- return false
-}
-
-func (p *Prebuilt) installable() bool {
- return p.properties.Installable == nil || proptools.Bool(p.properties.Installable)
-}
-
-func (p *Prebuilt) OutputFiles(tag string) (android.Paths, error) {
- switch tag {
- case "":
- return android.Paths{p.outputApex}, nil
- default:
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
- }
-}
-
-func (p *Prebuilt) InstallFilename() string {
- return proptools.StringDefault(p.properties.Filename, p.BaseModuleName()+imageApexSuffix)
-}
-
-func (p *Prebuilt) Name() string {
- return p.prebuiltCommon.prebuilt.Name(p.ModuleBase.Name())
-}
-
-// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
-//
-// If this needs to make files from within a `.apex` file available for use by other Soong modules,
-// e.g. make dex implementation jars available for java_import modules isted in exported_java_libs,
-// it does so as follows:
-//
-// 1. It creates a `deapexer` module that actually extracts the files from the `.apex` file and
-// makes them available for use by other modules, at both Soong and ninja levels.
-//
-// 2. It adds a dependency onto those modules and creates an apex specific variant similar to what
-// an `apex` module does. That ensures that code which looks for specific apex variant, e.g.
-// dexpreopt, will work the same way from source and prebuilt.
-//
-// 3. The `deapexer` module adds a dependency from the modules that require the exported files onto
-// itself so that they can retrieve the file paths to those files.
-//
-func PrebuiltFactory() android.Module {
- module := &Prebuilt{}
- module.AddProperties(&module.properties)
- android.InitPrebuiltModuleWithSrcSupplier(module, module.properties.prebuiltApexSelector, "src")
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
-
- android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- props := struct {
- Name *string
- }{
- Name: proptools.StringPtr(module.BaseModuleName() + ".deapexer"),
- }
- ctx.CreateModule(privateDeapexerFactory,
- &props,
- &module.properties.ApexFileProperties,
- &module.properties.DeapexerProperties,
- )
- })
-
- return module
-}
-
-func prebuiltApexExportedModuleName(ctx android.BottomUpMutatorContext, name string) string {
- // The prebuilt_apex should be depending on prebuilt modules but as this runs after
- // prebuilt_rename the prebuilt module may or may not be using the prebuilt_ prefixed named. So,
- // check to see if the prefixed name is in use first, if it is then use that, otherwise assume
- // the unprefixed name is the one to use. If the unprefixed one turns out to be a source module
- // and not a renamed prebuilt module then that will be detected and reported as an error when
- // processing the dependency in ApexInfoMutator().
- prebuiltName := android.PrebuiltNameFromSource(name)
- if ctx.OtherModuleExists(prebuiltName) {
- name = prebuiltName
- }
- return name
-}
-
-type exportedDependencyTag struct {
- blueprint.BaseDependencyTag
- name string
-}
-
-// Mark this tag so dependencies that use it are excluded from visibility enforcement.
-//
-// This does allow any prebuilt_apex to reference any module which does open up a small window for
-// restricted visibility modules to be referenced from the wrong prebuilt_apex. However, doing so
-// avoids opening up a much bigger window by widening the visibility of modules that need files
-// provided by the prebuilt_apex to include all the possible locations they may be defined, which
-// could include everything below vendor/.
-//
-// A prebuilt_apex that references a module via this tag will have to contain the appropriate files
-// corresponding to that module, otherwise it will fail when attempting to retrieve the files from
-// the .apex file. It will also have to be included in the module's apex_available property too.
-// That makes it highly unlikely that a prebuilt_apex would reference a restricted module
-// incorrectly.
-func (t exportedDependencyTag) ExcludeFromVisibilityEnforcement() {}
-
-var (
- exportedJavaLibTag = exportedDependencyTag{name: "exported_java_lib"}
-)
-
-func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
+func (p *prebuiltCommon) deapexerDeps(ctx android.BottomUpMutatorContext) {
// Add dependencies onto the java modules that represent the java libraries that are provided by
// and exported from this prebuilt apex.
- for _, lib := range p.properties.Exported_java_libs {
+ for _, lib := range p.deapexerProperties.Exported_java_libs {
dep := prebuiltApexExportedModuleName(ctx, lib)
ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), exportedJavaLibTag, dep)
}
}
-var _ ApexInfoMutator = (*Prebuilt)(nil)
-
-// ApexInfoMutator marks any modules for which this apex exports a file as requiring an apex
+// apexInfoMutator marks any modules for which this apex exports a file as requiring an apex
// specific variant and checks that they are supported.
//
// The apexMutator will ensure that the ApexInfo objects passed to BuildForApex(ApexInfo) are
@@ -317,7 +128,7 @@
// * The build cost of a prebuilt_apex variant is generally low as at worst it will involve some
// extra copying of files. Contrast that with source apex modules that has to build each variant
// from source.
-func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
+func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) {
// Collect direct dependencies into contents.
contents := make(map[string]android.ApexMembership)
@@ -373,9 +184,289 @@
}
}
+// prebuiltApexSelectorModule is a private module type that is only created by the prebuilt_apex
+// module. It selects the apex to use and makes it available for use by prebuilt_apex and the
+// deapexer.
+type prebuiltApexSelectorModule struct {
+ android.ModuleBase
+
+ apexFileProperties ApexFileProperties
+
+ inputApex android.Path
+}
+
+func privateApexSelectorModuleFactory() android.Module {
+ module := &prebuiltApexSelectorModule{}
+ module.AddProperties(
+ &module.apexFileProperties,
+ )
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
+
+func (p *prebuiltApexSelectorModule) Srcs() android.Paths {
+ return android.Paths{p.inputApex}
+}
+
+func (p *prebuiltApexSelectorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ p.inputApex = android.SingleSourcePathFromSupplier(ctx, p.apexFileProperties.prebuiltApexSelector, "src")
+}
+
+type Prebuilt struct {
+ android.ModuleBase
+ prebuiltCommon
+
+ properties PrebuiltProperties
+ selectedApexProperties SelectedApexProperties
+
+ inputApex android.Path
+ installDir android.InstallPath
+ installFilename string
+ outputApex android.WritablePath
+
+ // list of commands to create symlinks for backward compatibility.
+ // these commands will be attached as LOCAL_POST_INSTALL_CMD
+ compatSymlinks []string
+}
+
+type ApexFileProperties struct {
+ // the path to the prebuilt .apex file to import.
+ //
+ // This cannot be marked as `android:"arch_variant"` because the `prebuilt_apex` is only mutated
+ // for android_common. That is so that it will have the same arch variant as, and so be compatible
+ // with, the source `apex` module type that it replaces.
+ Src *string `android:"path"`
+ Arch struct {
+ Arm struct {
+ Src *string `android:"path"`
+ }
+ Arm64 struct {
+ Src *string `android:"path"`
+ }
+ X86 struct {
+ Src *string `android:"path"`
+ }
+ X86_64 struct {
+ Src *string `android:"path"`
+ }
+ }
+}
+
+// prebuiltApexSelector selects the correct prebuilt APEX file for the build target.
+//
+// The ctx parameter can be for any module not just the prebuilt module so care must be taken not
+// to use methods on it that are specific to the current module.
+//
+// See the ApexFileProperties.Src property.
+func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, prebuilt android.Module) []string {
+ multiTargets := prebuilt.MultiTargets()
+ if len(multiTargets) != 1 {
+ ctx.OtherModuleErrorf(prebuilt, "compile_multilib shouldn't be \"both\" for prebuilt_apex")
+ return nil
+ }
+ var src string
+ switch multiTargets[0].Arch.ArchType {
+ case android.Arm:
+ src = String(p.Arch.Arm.Src)
+ case android.Arm64:
+ src = String(p.Arch.Arm64.Src)
+ case android.X86:
+ src = String(p.Arch.X86.Src)
+ case android.X86_64:
+ src = String(p.Arch.X86_64.Src)
+ }
+ if src == "" {
+ src = String(p.Src)
+ }
+
+ if src == "" {
+ ctx.OtherModuleErrorf(prebuilt, "prebuilt_apex does not support %q", multiTargets[0].Arch.String())
+ // Drop through to return an empty string as the src (instead of nil) to avoid the prebuilt
+ // logic from reporting a more general, less useful message.
+ }
+
+ return []string{src}
+}
+
+type PrebuiltProperties struct {
+ ApexFileProperties
+
+ Installable *bool
+ // Optional name for the installed apex. If unspecified, name of the
+ // module is used as the file name
+ Filename *string
+
+ // Names of modules to be overridden. Listed modules can only be other binaries
+ // (in Make or Soong).
+ // This does not completely prevent installation of the overridden binaries, but if both
+ // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+ // from PRODUCT_PACKAGES.
+ Overrides []string
+}
+
+func (a *Prebuilt) hasSanitizedSource(sanitizer string) bool {
+ return false
+}
+
+func (p *Prebuilt) installable() bool {
+ return p.properties.Installable == nil || proptools.Bool(p.properties.Installable)
+}
+
+func (p *Prebuilt) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return android.Paths{p.outputApex}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
+func (p *Prebuilt) InstallFilename() string {
+ return proptools.StringDefault(p.properties.Filename, p.BaseModuleName()+imageApexSuffix)
+}
+
+func (p *Prebuilt) Name() string {
+ return p.prebuiltCommon.prebuilt.Name(p.ModuleBase.Name())
+}
+
+// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
+//
+// If this needs to make files from within a `.apex` file available for use by other Soong modules,
+// e.g. make dex implementation jars available for java_import modules isted in exported_java_libs,
+// it does so as follows:
+//
+// 1. It creates a `deapexer` module that actually extracts the files from the `.apex` file and
+// makes them available for use by other modules, at both Soong and ninja levels.
+//
+// 2. It adds a dependency onto those modules and creates an apex specific variant similar to what
+// an `apex` module does. That ensures that code which looks for specific apex variant, e.g.
+// dexpreopt, will work the same way from source and prebuilt.
+//
+// 3. The `deapexer` module adds a dependency from the modules that require the exported files onto
+// itself so that they can retrieve the file paths to those files.
+//
+// It also creates a child module `selector` that is responsible for selecting the appropriate
+// input apex for both the prebuilt_apex and the deapexer. That is needed for a couple of reasons:
+// 1. To dedup the selection logic so it only runs in one module.
+// 2. To allow the deapexer to be wired up to a different source for the input apex, e.g. an
+// `apex_set`.
+//
+// prebuilt_apex
+// / | \
+// / | \
+// V | V
+// selector <--- deapexer <--- exported java lib
+//
+func PrebuiltFactory() android.Module {
+ module := &Prebuilt{}
+ module.AddProperties(&module.properties, &module.deapexerProperties, &module.selectedApexProperties)
+ android.InitSingleSourcePrebuiltModule(module, &module.selectedApexProperties, "Selected_apex")
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ baseModuleName := module.BaseModuleName()
+
+ apexSelectorModuleName := apexSelectorModuleName(baseModuleName)
+ createApexSelectorModule(ctx, apexSelectorModuleName, &module.properties.ApexFileProperties)
+
+ apexFileSource := ":" + apexSelectorModuleName
+ if len(module.deapexerProperties.Exported_java_libs) != 0 {
+ createDeapexerModule(ctx, deapexerModuleName(baseModuleName), apexFileSource, &module.deapexerProperties)
+ }
+
+ // Add a source reference to retrieve the selected apex from the selector module.
+ module.selectedApexProperties.Selected_apex = proptools.StringPtr(apexFileSource)
+ })
+
+ return module
+}
+
+func createApexSelectorModule(ctx android.LoadHookContext, name string, apexFileProperties *ApexFileProperties) {
+ props := struct {
+ Name *string
+ }{
+ Name: proptools.StringPtr(name),
+ }
+
+ ctx.CreateModule(privateApexSelectorModuleFactory,
+ &props,
+ apexFileProperties,
+ )
+}
+
+func createDeapexerModule(ctx android.LoadHookContext, deapexerName string, apexFileSource string, deapexerProperties *DeapexerProperties) {
+ props := struct {
+ Name *string
+ Selected_apex *string
+ }{
+ Name: proptools.StringPtr(deapexerName),
+ Selected_apex: proptools.StringPtr(apexFileSource),
+ }
+ ctx.CreateModule(privateDeapexerFactory,
+ &props,
+ deapexerProperties,
+ )
+}
+
+func deapexerModuleName(baseModuleName string) string {
+ return baseModuleName + ".deapexer"
+}
+
+func apexSelectorModuleName(baseModuleName string) string {
+ return baseModuleName + ".apex.selector"
+}
+
+func prebuiltApexExportedModuleName(ctx android.BottomUpMutatorContext, name string) string {
+ // The prebuilt_apex should be depending on prebuilt modules but as this runs after
+ // prebuilt_rename the prebuilt module may or may not be using the prebuilt_ prefixed named. So,
+ // check to see if the prefixed name is in use first, if it is then use that, otherwise assume
+ // the unprefixed name is the one to use. If the unprefixed one turns out to be a source module
+ // and not a renamed prebuilt module then that will be detected and reported as an error when
+ // processing the dependency in ApexInfoMutator().
+ prebuiltName := android.PrebuiltNameFromSource(name)
+ if ctx.OtherModuleExists(prebuiltName) {
+ name = prebuiltName
+ }
+ return name
+}
+
+type exportedDependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
+
+// Mark this tag so dependencies that use it are excluded from visibility enforcement.
+//
+// This does allow any prebuilt_apex to reference any module which does open up a small window for
+// restricted visibility modules to be referenced from the wrong prebuilt_apex. However, doing so
+// avoids opening up a much bigger window by widening the visibility of modules that need files
+// provided by the prebuilt_apex to include all the possible locations they may be defined, which
+// could include everything below vendor/.
+//
+// A prebuilt_apex that references a module via this tag will have to contain the appropriate files
+// corresponding to that module, otherwise it will fail when attempting to retrieve the files from
+// the .apex file. It will also have to be included in the module's apex_available property too.
+// That makes it highly unlikely that a prebuilt_apex would reference a restricted module
+// incorrectly.
+func (t exportedDependencyTag) ExcludeFromVisibilityEnforcement() {}
+
+var (
+ exportedJavaLibTag = exportedDependencyTag{name: "exported_java_lib"}
+)
+
+func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
+ p.deapexerDeps(ctx)
+}
+
+var _ ApexInfoMutator = (*Prebuilt)(nil)
+
+func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
+ p.apexInfoMutator(mctx)
+}
+
func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// TODO(jungjw): Check the key validity.
- p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
+ p.inputApex = android.OptionalPathForModuleSrc(ctx, p.selectedApexProperties.Selected_apex).Path()
p.installDir = android.PathForModuleInstall(ctx, "apex")
p.installFilename = p.InstallFilename()
if !strings.HasSuffix(p.installFilename, imageApexSuffix) {
@@ -424,6 +515,49 @@
}}
}
+// prebuiltApexExtractorModule is a private module type that is only created by the prebuilt_apex
+// module. It extracts the correct apex to use and makes it available for use by apex_set.
+type prebuiltApexExtractorModule struct {
+ android.ModuleBase
+
+ properties ApexExtractorProperties
+
+ extractedApex android.WritablePath
+}
+
+func privateApexExtractorModuleFactory() android.Module {
+ module := &prebuiltApexExtractorModule{}
+ module.AddProperties(
+ &module.properties,
+ )
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
+
+func (p *prebuiltApexExtractorModule) Srcs() android.Paths {
+ return android.Paths{p.extractedApex}
+}
+
+func (p *prebuiltApexExtractorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ srcsSupplier := func(ctx android.BaseModuleContext, prebuilt android.Module) []string {
+ return p.properties.prebuiltSrcs(ctx)
+ }
+ apexSet := android.SingleSourcePathFromSupplier(ctx, srcsSupplier, "set")
+ p.extractedApex = android.PathForModuleOut(ctx, "extracted", apexSet.Base())
+ ctx.Build(pctx,
+ android.BuildParams{
+ Rule: extractMatchingApex,
+ Description: "Extract an apex from an apex set",
+ Inputs: android.Paths{apexSet},
+ Output: p.extractedApex,
+ Args: map[string]string{
+ "abis": strings.Join(java.SupportedAbis(ctx), ","),
+ "allow-prereleased": strconv.FormatBool(proptools.Bool(p.properties.Prerelease)),
+ "sdk-version": ctx.Config().PlatformSdkVersion().String(),
+ },
+ })
+}
+
type ApexSet struct {
android.ModuleBase
prebuiltCommon
@@ -442,7 +576,7 @@
postInstallCommands []string
}
-type ApexSetProperties struct {
+type ApexExtractorProperties struct {
// the .apks file path that contains prebuilt apex files to be extracted.
Set *string
@@ -458,6 +592,37 @@
}
}
+ // apexes in this set use prerelease SDK version
+ Prerelease *bool
+}
+
+func (e *ApexExtractorProperties) prebuiltSrcs(ctx android.BaseModuleContext) []string {
+ var srcs []string
+ if e.Set != nil {
+ srcs = append(srcs, *e.Set)
+ }
+
+ var sanitizers []string
+ if ctx.Host() {
+ sanitizers = ctx.Config().SanitizeHost()
+ } else {
+ sanitizers = ctx.Config().SanitizeDevice()
+ }
+
+ if android.InList("address", sanitizers) && e.Sanitized.Address.Set != nil {
+ srcs = append(srcs, *e.Sanitized.Address.Set)
+ } else if android.InList("hwaddress", sanitizers) && e.Sanitized.Hwaddress.Set != nil {
+ srcs = append(srcs, *e.Sanitized.Hwaddress.Set)
+ } else if e.Sanitized.None.Set != nil {
+ srcs = append(srcs, *e.Sanitized.None.Set)
+ }
+
+ return srcs
+}
+
+type ApexSetProperties struct {
+ ApexExtractorProperties
+
// whether the extracted apex file installable.
Installable *bool
@@ -471,33 +636,6 @@
// binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
// from PRODUCT_PACKAGES.
Overrides []string
-
- // apexes in this set use prerelease SDK version
- Prerelease *bool
-}
-
-func (a *ApexSet) prebuiltSrcs(ctx android.BaseModuleContext) []string {
- var srcs []string
- if a.properties.Set != nil {
- srcs = append(srcs, *a.properties.Set)
- }
-
- var sanitizers []string
- if ctx.Host() {
- sanitizers = ctx.Config().SanitizeHost()
- } else {
- sanitizers = ctx.Config().SanitizeDevice()
- }
-
- if android.InList("address", sanitizers) && a.properties.Sanitized.Address.Set != nil {
- srcs = append(srcs, *a.properties.Sanitized.Address.Set)
- } else if android.InList("hwaddress", sanitizers) && a.properties.Sanitized.Hwaddress.Set != nil {
- srcs = append(srcs, *a.properties.Sanitized.Hwaddress.Set)
- } else if a.properties.Sanitized.None.Set != nil {
- srcs = append(srcs, *a.properties.Sanitized.None.Set)
- }
-
- return srcs
}
func (a *ApexSet) hasSanitizedSource(sanitizer string) bool {
@@ -530,15 +668,41 @@
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
func apexSetFactory() android.Module {
module := &ApexSet{}
- module.AddProperties(&module.properties)
+ module.AddProperties(&module.properties, &module.selectedApexProperties)
- srcsSupplier := func(ctx android.BaseModuleContext, _ android.Module) []string {
- return module.prebuiltSrcs(ctx)
+ android.InitSingleSourcePrebuiltModule(module, &module.selectedApexProperties, "Selected_apex")
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ baseModuleName := module.BaseModuleName()
+
+ apexExtractorModuleName := apexExtractorModuleName(baseModuleName)
+ createApexExtractorModule(ctx, apexExtractorModuleName, &module.properties.ApexExtractorProperties)
+
+ apexFileSource := ":" + apexExtractorModuleName
+
+ // After passing the arch specific src properties to the creating the apex selector module
+ module.selectedApexProperties.Selected_apex = proptools.StringPtr(apexFileSource)
+ })
+
+ return module
+}
+
+func createApexExtractorModule(ctx android.LoadHookContext, name string, apexExtractorProperties *ApexExtractorProperties) {
+ props := struct {
+ Name *string
+ }{
+ Name: proptools.StringPtr(name),
}
- android.InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "set")
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
- return module
+ ctx.CreateModule(privateApexExtractorModuleFactory,
+ &props,
+ apexExtractorProperties,
+ )
+}
+
+func apexExtractorModuleName(baseModuleName string) string {
+ return baseModuleName + ".apex.extractor"
}
func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -547,20 +711,13 @@
ctx.ModuleErrorf("filename should end in %s for apex_set", imageApexSuffix)
}
- apexSet := a.prebuiltCommon.prebuilt.SingleSourcePath(ctx)
+ inputApex := android.OptionalPathForModuleSrc(ctx, a.selectedApexProperties.Selected_apex).Path()
a.outputApex = android.PathForModuleOut(ctx, a.installFilename)
- ctx.Build(pctx,
- android.BuildParams{
- Rule: extractMatchingApex,
- Description: "Extract an apex from an apex set",
- Inputs: android.Paths{apexSet},
- Output: a.outputApex,
- Args: map[string]string{
- "abis": strings.Join(java.SupportedAbis(ctx), ","),
- "allow-prereleased": strconv.FormatBool(proptools.Bool(a.properties.Prerelease)),
- "sdk-version": ctx.Config().PlatformSdkVersion().String(),
- },
- })
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: inputApex,
+ Output: a.outputApex,
+ })
if a.prebuiltCommon.checkForceDisable(ctx) {
a.HideFromMake()
diff --git a/bazel/properties.go b/bazel/properties.go
index 2440ca1..148386f 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -79,6 +79,63 @@
return uniqueLabelList
}
+// Subtract needle from haystack
+func SubtractStrings(haystack []string, needle []string) []string {
+ // This is really a set
+ remainder := make(map[string]bool)
+
+ for _, s := range haystack {
+ remainder[s] = true
+ }
+ for _, s := range needle {
+ delete(remainder, s)
+ }
+
+ var strings []string
+ for s, _ := range remainder {
+ strings = append(strings, s)
+ }
+
+ sort.SliceStable(strings, func(i, j int) bool {
+ return strings[i] < strings[j]
+ })
+
+ return strings
+}
+
+// Subtract needle from haystack
+func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
+ // This is really a set
+ remainder := make(map[Label]bool)
+
+ for _, label := range haystack {
+ remainder[label] = true
+ }
+ for _, label := range needle {
+ delete(remainder, label)
+ }
+
+ var labels []Label
+ for label, _ := range remainder {
+ labels = append(labels, label)
+ }
+
+ sort.SliceStable(labels, func(i, j int) bool {
+ return labels[i].Label < labels[j].Label
+ })
+
+ return labels
+}
+
+// Subtract needle from haystack
+func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
+ var result LabelList
+ result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
+ // NOTE: Excludes are intentionally not subtracted
+ result.Excludes = haystack.Excludes
+ return result
+}
+
const (
// ArchType names in arch.go
ARCH_ARM = "arm"
@@ -257,6 +314,12 @@
OsValues stringListOsValues
}
+// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
+func MakeStringListAttribute(value []string) StringListAttribute {
+ // NOTE: These strings are not necessarily unique or sorted.
+ return StringListAttribute{Value: value}
+}
+
// Arch-specific string_list typed Bazel attribute values. This should correspond
// to the types of architectures supported for compilation in arch.go.
type stringListArchValues struct {
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 0fcb904..56840ef 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -46,6 +46,82 @@
}
}
+func TestSubtractStrings(t *testing.T) {
+ testCases := []struct {
+ haystack []string
+ needle []string
+ expectedResult []string
+ }{
+ {
+ haystack: []string{
+ "a",
+ "b",
+ "c",
+ },
+ needle: []string{
+ "a",
+ },
+ expectedResult: []string{
+ "b", "c",
+ },
+ },
+ }
+ for _, tc := range testCases {
+ actualResult := SubtractStrings(tc.haystack, tc.needle)
+ if !reflect.DeepEqual(tc.expectedResult, actualResult) {
+ t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult)
+ }
+ }
+}
+
+func TestSubtractBazelLabelList(t *testing.T) {
+ testCases := []struct {
+ haystack LabelList
+ needle LabelList
+ expectedResult LabelList
+ }{
+ {
+ haystack: LabelList{
+ Includes: []Label{
+ {Label: "a"},
+ {Label: "b"},
+ {Label: "c"},
+ },
+ Excludes: []Label{
+ {Label: "x"},
+ {Label: "y"},
+ {Label: "z"},
+ },
+ },
+ needle: LabelList{
+ Includes: []Label{
+ {Label: "a"},
+ },
+ Excludes: []Label{
+ {Label: "z"},
+ },
+ },
+ // NOTE: Excludes are intentionally not subtracted
+ expectedResult: LabelList{
+ Includes: []Label{
+ {Label: "b"},
+ {Label: "c"},
+ },
+ Excludes: []Label{
+ {Label: "x"},
+ {Label: "y"},
+ {Label: "z"},
+ },
+ },
+ },
+ }
+ for _, tc := range testCases {
+ actualResult := SubtractBazelLabelList(tc.haystack, tc.needle)
+ if !reflect.DeepEqual(tc.expectedResult, actualResult) {
+ t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult)
+ }
+ }
+}
func TestUniqueBazelLabelList(t *testing.T) {
testCases := []struct {
originalLabelList LabelList
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index dbea37a..74226ae 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -86,14 +86,17 @@
moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
filesystem: map[string]string{
- "lib-1/lib1a.h": "",
- "lib-1/lib1b.h": "",
- "lib-2/lib2a.h": "",
- "lib-2/lib2b.h": "",
- "dir-1/dir1a.h": "",
- "dir-1/dir1b.h": "",
- "dir-2/dir2a.h": "",
- "dir-2/dir2b.h": "",
+ "lib-1/lib1a.h": "",
+ "lib-1/lib1b.h": "",
+ "lib-2/lib2a.h": "",
+ "lib-2/lib2b.h": "",
+ "dir-1/dir1a.h": "",
+ "dir-1/dir1b.h": "",
+ "dir-2/dir2a.h": "",
+ "dir-2/dir2b.h": "",
+ "arch_arm64_exported_include_dir/a.h": "",
+ "arch_x86_exported_include_dir/b.h": "",
+ "arch_x86_64_exported_include_dir/c.h": "",
},
bp: soongCcLibraryPreamble + `
cc_library_headers {
@@ -111,6 +114,19 @@
export_include_dirs: ["dir-1", "dir-2"],
header_libs: ["lib-1", "lib-2"],
+ arch: {
+ arm64: {
+ // We expect dir-1 headers to be dropped, because dir-1 is already in export_include_dirs
+ export_include_dirs: ["arch_arm64_exported_include_dir", "dir-1"],
+ },
+ x86: {
+ export_include_dirs: ["arch_x86_exported_include_dir"],
+ },
+ x86_64: {
+ export_include_dirs: ["arch_x86_64_exported_include_dir"],
+ },
+ },
+
// TODO: Also support export_header_lib_headers
}`,
expectedBazelTargets: []string{`cc_library_headers(
@@ -124,11 +140,33 @@
"dir-1/dir1b.h",
"dir-2/dir2a.h",
"dir-2/dir2b.h",
- ],
+ ] + select({
+ "//build/bazel/platforms/arch:arm64": [
+ "arch_arm64_exported_include_dir/a.h",
+ ],
+ "//build/bazel/platforms/arch:x86": [
+ "arch_x86_exported_include_dir/b.h",
+ ],
+ "//build/bazel/platforms/arch:x86_64": [
+ "arch_x86_64_exported_include_dir/c.h",
+ ],
+ "//conditions:default": [],
+ }),
includes = [
"dir-1",
"dir-2",
- ],
+ ] + select({
+ "//build/bazel/platforms/arch:arm64": [
+ "arch_arm64_exported_include_dir",
+ ],
+ "//build/bazel/platforms/arch:x86": [
+ "arch_x86_exported_include_dir",
+ ],
+ "//build/bazel/platforms/arch:x86_64": [
+ "arch_x86_64_exported_include_dir",
+ ],
+ "//conditions:default": [],
+ }),
)`, `cc_library_headers(
name = "lib-1",
hdrs = [
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 497d227..cffeb24 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -16,6 +16,7 @@
import (
"android/soong/android"
"android/soong/bazel"
+ "strings"
)
// bp2build functions and helpers for converting cc_* modules to Bazel.
@@ -109,23 +110,78 @@
return ret
}
+func bp2BuildListHeadersInDir(ctx android.TopDownMutatorContext, includeDir string) bazel.LabelList {
+ var globInfix string
+
+ if includeDir == "." {
+ globInfix = ""
+ } else {
+ globInfix = "/**"
+ }
+
+ var includeDirGlobs []string
+ includeDirGlobs = append(includeDirGlobs, includeDir+globInfix+"/*.h")
+ includeDirGlobs = append(includeDirGlobs, includeDir+globInfix+"/*.inc")
+ includeDirGlobs = append(includeDirGlobs, includeDir+globInfix+"/*.hpp")
+
+ return android.BazelLabelForModuleSrc(ctx, includeDirGlobs)
+}
+
+// Bazel wants include paths to be relative to the module
+func bp2BuildMakePathsRelativeToModule(ctx android.TopDownMutatorContext, paths []string) []string {
+ var relativePaths []string
+ for _, path := range paths {
+ relativePath := strings.TrimPrefix(path, ctx.ModuleDir()+"/")
+ relativePaths = append(relativePaths, relativePath)
+ }
+ return relativePaths
+}
+
// bp2BuildParseExportedIncludes creates a label list attribute contains the
// exported included directories of a module.
-func bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) (bazel.LabelListAttribute, bazel.LabelListAttribute) {
+func bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) (bazel.StringListAttribute, bazel.LabelListAttribute) {
libraryDecorator := module.linker.(*libraryDecorator)
includeDirs := libraryDecorator.flagExporter.Properties.Export_system_include_dirs
includeDirs = append(includeDirs, libraryDecorator.flagExporter.Properties.Export_include_dirs...)
+ includeDirs = bp2BuildMakePathsRelativeToModule(ctx, includeDirs)
+ includeDirsAttribute := bazel.MakeStringListAttribute(includeDirs)
- includeDirsLabels := android.BazelLabelForModuleSrc(ctx, includeDirs)
-
- var includeDirGlobs []string
+ var headersAttribute bazel.LabelListAttribute
+ var headers bazel.LabelList
for _, includeDir := range includeDirs {
- includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.h")
- includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.inc")
- includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.hpp")
+ headers.Append(bp2BuildListHeadersInDir(ctx, includeDir))
+ }
+ headers = bazel.UniqueBazelLabelList(headers)
+ headersAttribute.Value = headers
+
+ for arch, props := range module.GetArchProperties(&FlagExporterProperties{}) {
+ if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
+ archIncludeDirs := flagExporterProperties.Export_system_include_dirs
+ archIncludeDirs = append(archIncludeDirs, flagExporterProperties.Export_include_dirs...)
+ archIncludeDirs = bp2BuildMakePathsRelativeToModule(ctx, archIncludeDirs)
+
+ // To avoid duplicate includes when base includes + arch includes are combined
+ archIncludeDirs = bazel.SubtractStrings(archIncludeDirs, includeDirs)
+
+ if len(archIncludeDirs) > 0 {
+ includeDirsAttribute.SetValueForArch(arch.Name, archIncludeDirs)
+ }
+
+ var archHeaders bazel.LabelList
+ for _, archIncludeDir := range archIncludeDirs {
+ archHeaders.Append(bp2BuildListHeadersInDir(ctx, archIncludeDir))
+ }
+ archHeaders = bazel.UniqueBazelLabelList(archHeaders)
+
+ // To avoid duplicate headers when base headers + arch headers are combined
+ archHeaders = bazel.SubtractBazelLabelList(archHeaders, headers)
+
+ if len(archHeaders.Includes) > 0 || len(archHeaders.Excludes) > 0 {
+ headersAttribute.SetValueForArch(arch.Name, archHeaders)
+ }
+ }
}
- headersLabels := android.BazelLabelForModuleSrc(ctx, includeDirGlobs)
- return bazel.MakeLabelListAttribute(includeDirsLabels), bazel.MakeLabelListAttribute(headersLabels)
+ return includeDirsAttribute, headersAttribute
}
diff --git a/cc/library.go b/cc/library.go
index 11b6737..b49f1e5 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -2062,7 +2062,7 @@
Srcs bazel.LabelListAttribute
Deps bazel.LabelListAttribute
Linkstatic bool
- Includes bazel.LabelListAttribute
+ Includes bazel.StringListAttribute
Hdrs bazel.LabelListAttribute
}
@@ -2099,8 +2099,8 @@
if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
copts = baseCompilerProps.Cflags
srcs = baseCompilerProps.Srcs
- includeDirs = baseCompilerProps.Include_dirs
- localIncludeDirs = baseCompilerProps.Local_include_dirs
+ includeDirs = bp2BuildMakePathsRelativeToModule(ctx, baseCompilerProps.Include_dirs)
+ localIncludeDirs = bp2BuildMakePathsRelativeToModule(ctx, baseCompilerProps.Local_include_dirs)
break
}
}
@@ -2122,14 +2122,13 @@
depsLabels := android.BazelLabelForModuleDeps(ctx, allDeps)
+ exportedIncludes, exportedIncludesHeaders := bp2BuildParseExportedIncludes(ctx, module)
+
// FIXME: Unify absolute vs relative paths
// FIXME: Use -I copts instead of setting includes= ?
- allIncludes := includeDirs
- allIncludes = append(allIncludes, localIncludeDirs...)
- includesLabels := android.BazelLabelForModuleSrc(ctx, allIncludes)
-
- exportedIncludesLabels, exportedIncludesHeadersLabels := bp2BuildParseExportedIncludes(ctx, module)
- includesLabels.Append(exportedIncludesLabels.Value)
+ allIncludes := exportedIncludes
+ allIncludes.Value = append(allIncludes.Value, includeDirs...)
+ allIncludes.Value = append(allIncludes.Value, localIncludeDirs...)
headerLibsLabels := bp2BuildParseHeaderLibs(ctx, module)
depsLabels.Append(headerLibsLabels.Value)
@@ -2139,8 +2138,8 @@
Srcs: srcsLabels,
Deps: bazel.MakeLabelListAttribute(depsLabels),
Linkstatic: true,
- Includes: bazel.MakeLabelListAttribute(includesLabels),
- Hdrs: exportedIncludesHeadersLabels,
+ Includes: allIncludes,
+ Hdrs: exportedIncludesHeaders,
}
props := bazel.BazelTargetModuleProperties{
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 82af16a..d35748b 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -64,7 +64,7 @@
type bazelCcLibraryHeadersAttributes struct {
Copts bazel.StringListAttribute
Hdrs bazel.LabelListAttribute
- Includes bazel.LabelListAttribute
+ Includes bazel.StringListAttribute
Deps bazel.LabelListAttribute
}
@@ -95,15 +95,15 @@
return
}
- exportedIncludesLabels, exportedIncludesHeadersLabels := bp2BuildParseExportedIncludes(ctx, module)
+ exportedIncludes, exportedIncludesHeaders := bp2BuildParseExportedIncludes(ctx, module)
- headerLibsLabels := bp2BuildParseHeaderLibs(ctx, module)
+ headerLibs := bp2BuildParseHeaderLibs(ctx, module)
attrs := &bazelCcLibraryHeadersAttributes{
Copts: bp2BuildParseCflags(ctx, module),
- Includes: exportedIncludesLabels,
- Hdrs: exportedIncludesHeadersLabels,
- Deps: headerLibsLabels,
+ Includes: exportedIncludes,
+ Hdrs: exportedIncludesHeaders,
+ Deps: headerLibs,
}
props := bazel.BazelTargetModuleProperties{
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index bbb8896..af05102 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -337,7 +337,8 @@
for _, name := range names {
snapshotMap[name] = name +
getSnapshotNameSuffix(snapshotSuffix+moduleSuffix,
- s.baseSnapshot.version(), ctx.Arch().ArchType.Name)
+ s.baseSnapshot.version(),
+ ctx.DeviceConfig().Arches()[0].ArchType.String())
}
return snapshotMap
}
@@ -396,7 +397,7 @@
Target_arch string
// Suffix to be added to the module name when exporting to Android.mk, e.g. ".vendor".
- Androidmk_suffix string
+ Androidmk_suffix string `blueprint:"mutated"`
// Suffix to be added to the module name, e.g., vendor_shared,
// recovery_shared, etc.
@@ -417,6 +418,7 @@
// will be seen as "libbase.vendor_static.30.arm64" by Soong.
type baseSnapshotDecorator struct {
baseProperties baseSnapshotDecoratorProperties
+ image snapshotImage
}
func (p *baseSnapshotDecorator) Name(name string) string {
@@ -447,10 +449,21 @@
return p.baseProperties.Androidmk_suffix
}
+func (p *baseSnapshotDecorator) setSnapshotAndroidMkSuffix(ctx android.ModuleContext) {
+ if ctx.OtherModuleDependencyVariantExists([]blueprint.Variation{
+ {Mutator: "image", Variation: android.CoreVariation},
+ }, ctx.Module().(*Module).BaseModuleName()) {
+ p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix()
+ } else {
+ p.baseProperties.Androidmk_suffix = ""
+ }
+}
+
// Call this with a module suffix after creating a snapshot module, such as
// vendorSnapshotSharedSuffix, recoverySnapshotBinarySuffix, etc.
-func (p *baseSnapshotDecorator) init(m *Module, snapshotSuffix, moduleSuffix string) {
- p.baseProperties.ModuleSuffix = snapshotSuffix + moduleSuffix
+func (p *baseSnapshotDecorator) init(m *Module, image snapshotImage, moduleSuffix string) {
+ p.image = image
+ p.baseProperties.ModuleSuffix = image.moduleNameSuffix() + moduleSuffix
m.AddProperties(&p.baseProperties)
android.AddLoadHook(m, func(ctx android.LoadHookContext) {
vendorSnapshotLoadHook(ctx, p)
@@ -532,6 +545,8 @@
// As snapshots are prebuilts, this just returns the prebuilt binary after doing things which are
// done by normal library decorator, e.g. exporting flags.
func (p *snapshotLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
+ p.setSnapshotAndroidMkSuffix(ctx)
+
if p.header() {
return p.libraryDecorator.link(ctx, flags, deps, objs)
}
@@ -614,7 +629,7 @@
}
}
-func snapshotLibraryFactory(snapshotSuffix, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
+func snapshotLibraryFactory(image snapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
module, library := NewLibrary(android.DeviceSupported)
module.stl = nil
@@ -637,7 +652,7 @@
module.linker = prebuilt
module.installer = prebuilt
- prebuilt.init(module, snapshotSuffix, moduleSuffix)
+ prebuilt.init(module, image, moduleSuffix)
module.AddProperties(
&prebuilt.properties,
&prebuilt.sanitizerProperties,
@@ -651,7 +666,7 @@
// overrides the vendor variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
// is set.
func VendorSnapshotSharedFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton.moduleNameSuffix(), snapshotSharedSuffix)
+ module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton, snapshotSharedSuffix)
prebuilt.libraryDecorator.BuildOnlyShared()
return module.Init()
}
@@ -661,7 +676,7 @@
// overrides the recovery variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
// is set.
func RecoverySnapshotSharedFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton.moduleNameSuffix(), snapshotSharedSuffix)
+ module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, snapshotSharedSuffix)
prebuilt.libraryDecorator.BuildOnlyShared()
return module.Init()
}
@@ -671,7 +686,7 @@
// overrides the vendor variant of the cc static library with the same name, if BOARD_VNDK_VERSION
// is set.
func VendorSnapshotStaticFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton.moduleNameSuffix(), snapshotStaticSuffix)
+ module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton, snapshotStaticSuffix)
prebuilt.libraryDecorator.BuildOnlyStatic()
return module.Init()
}
@@ -681,7 +696,7 @@
// overrides the recovery variant of the cc static library with the same name, if BOARD_VNDK_VERSION
// is set.
func RecoverySnapshotStaticFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton.moduleNameSuffix(), snapshotStaticSuffix)
+ module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, snapshotStaticSuffix)
prebuilt.libraryDecorator.BuildOnlyStatic()
return module.Init()
}
@@ -691,7 +706,7 @@
// overrides the vendor variant of the cc header library with the same name, if BOARD_VNDK_VERSION
// is set.
func VendorSnapshotHeaderFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton.moduleNameSuffix(), snapshotHeaderSuffix)
+ module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton, snapshotHeaderSuffix)
prebuilt.libraryDecorator.HeaderOnly()
return module.Init()
}
@@ -701,7 +716,7 @@
// overrides the recovery variant of the cc header library with the same name, if BOARD_VNDK_VERSION
// is set.
func RecoverySnapshotHeaderFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton.moduleNameSuffix(), snapshotHeaderSuffix)
+ module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, snapshotHeaderSuffix)
prebuilt.libraryDecorator.HeaderOnly()
return module.Init()
}
@@ -739,6 +754,8 @@
// cc modules' link functions are to link compiled objects into final binaries.
// As snapshots are prebuilts, this just returns the prebuilt binary
func (p *snapshotBinaryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
+ p.setSnapshotAndroidMkSuffix(ctx)
+
if !p.matchesWithDevice(ctx.DeviceConfig()) {
return nil
}
@@ -767,17 +784,17 @@
// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_binary
// overrides the vendor variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
func VendorSnapshotBinaryFactory() android.Module {
- return snapshotBinaryFactory(vendorSnapshotImageSingleton.moduleNameSuffix(), snapshotBinarySuffix)
+ return snapshotBinaryFactory(vendorSnapshotImageSingleton, snapshotBinarySuffix)
}
// recovery_snapshot_binary is a special prebuilt executable binary which is auto-generated by
// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_binary
// overrides the recovery variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
func RecoverySnapshotBinaryFactory() android.Module {
- return snapshotBinaryFactory(recoverySnapshotImageSingleton.moduleNameSuffix(), snapshotBinarySuffix)
+ return snapshotBinaryFactory(recoverySnapshotImageSingleton, snapshotBinarySuffix)
}
-func snapshotBinaryFactory(snapshotSuffix, moduleSuffix string) android.Module {
+func snapshotBinaryFactory(image snapshotImage, moduleSuffix string) android.Module {
module, binary := NewBinary(android.DeviceSupported)
binary.baseLinker.Properties.No_libcrt = BoolPtr(true)
binary.baseLinker.Properties.Nocrt = BoolPtr(true)
@@ -796,7 +813,7 @@
module.stl = nil
module.linker = prebuilt
- prebuilt.init(module, snapshotSuffix, moduleSuffix)
+ prebuilt.init(module, image, moduleSuffix)
module.AddProperties(&prebuilt.properties)
return module.Init()
}
@@ -832,6 +849,8 @@
// cc modules' link functions are to link compiled objects into final binaries.
// As snapshots are prebuilts, this just returns the prebuilt binary
func (p *snapshotObjectLinker) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
+ p.setSnapshotAndroidMkSuffix(ctx)
+
if !p.matchesWithDevice(ctx.DeviceConfig()) {
return nil
}
@@ -856,7 +875,7 @@
}
module.linker = prebuilt
- prebuilt.init(module, vendorSnapshotImageSingleton.moduleNameSuffix(), snapshotObjectSuffix)
+ prebuilt.init(module, vendorSnapshotImageSingleton, snapshotObjectSuffix)
module.AddProperties(&prebuilt.properties)
return module.Init()
}
@@ -874,7 +893,7 @@
}
module.linker = prebuilt
- prebuilt.init(module, recoverySnapshotImageSingleton.moduleNameSuffix(), snapshotObjectSuffix)
+ prebuilt.init(module, recoverySnapshotImageSingleton, snapshotObjectSuffix)
module.AddProperties(&prebuilt.properties)
return module.Init()
}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 4014fe0..3d31be4 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -238,7 +238,6 @@
type snapshotJsonFlags struct {
ModuleName string `json:",omitempty"`
RelativeInstallPath string `json:",omitempty"`
- AndroidMkSuffix string `json:",omitempty"`
// library flags
ExportedDirs []string `json:",omitempty"`
@@ -352,7 +351,6 @@
} else {
prop.RelativeInstallPath = m.RelativeInstallPath()
}
- prop.AndroidMkSuffix = m.Properties.SubName
prop.RuntimeLibs = m.Properties.SnapshotRuntimeLibs
prop.Required = m.RequiredModuleNames()
for _, path := range m.InitRc() {
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 20cd031..8f77c28 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -271,7 +271,6 @@
enabled: true,
},
nocrt: true,
- compile_multilib: "64",
}
cc_library {
@@ -281,7 +280,6 @@
no_libcrt: true,
stl: "none",
system_shared_libs: [],
- compile_multilib: "64",
}
cc_library {
@@ -291,6 +289,25 @@
no_libcrt: true,
stl: "none",
system_shared_libs: [],
+ }
+
+ cc_library {
+ name: "lib32",
+ vendor: true,
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
+ system_shared_libs: [],
+ compile_multilib: "32",
+ }
+
+ cc_library {
+ name: "lib64",
+ vendor: true,
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
+ system_shared_libs: [],
compile_multilib: "64",
}
@@ -301,7 +318,16 @@
no_libcrt: true,
stl: "none",
system_shared_libs: [],
- compile_multilib: "64",
+ }
+
+ cc_binary {
+ name: "bin32",
+ vendor: true,
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
+ system_shared_libs: [],
+ compile_multilib: "32",
}
`
@@ -320,6 +346,10 @@
srcs: ["libvndk.so"],
export_include_dirs: ["include/libvndk"],
},
+ arm: {
+ srcs: ["libvndk.so"],
+ export_include_dirs: ["include/libvndk"],
+ },
},
}
@@ -338,6 +368,28 @@
srcs: ["libvndk.so"],
export_include_dirs: ["include/libvndk"],
},
+ arm: {
+ srcs: ["libvndk.so"],
+ export_include_dirs: ["include/libvndk"],
+ },
+ },
+ }
+
+ // different arch snapshot which has to be ignored
+ vndk_prebuilt_shared {
+ name: "libvndk",
+ version: "28",
+ target_arch: "arm",
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ },
+ arch: {
+ arm: {
+ srcs: ["libvndk.so"],
+ export_include_dirs: ["include/libvndk"],
+ },
},
}
`
@@ -350,7 +402,6 @@
no_libcrt: true,
stl: "none",
system_shared_libs: [],
- compile_multilib: "64",
}
cc_library_shared {
@@ -362,7 +413,14 @@
system_shared_libs: [],
shared_libs: ["libvndk", "libvendor_available"],
static_libs: ["libvendor", "libvendor_without_snapshot"],
- compile_multilib: "64",
+ arch: {
+ arm64: {
+ shared_libs: ["lib64"],
+ },
+ arm: {
+ shared_libs: ["lib32"],
+ },
+ },
srcs: ["client.cpp"],
}
@@ -374,41 +432,69 @@
stl: "none",
system_shared_libs: [],
static_libs: ["libvndk"],
- compile_multilib: "64",
srcs: ["bin.cpp"],
}
vendor_snapshot {
name: "vendor_snapshot",
- compile_multilib: "first",
version: "28",
- vndk_libs: [
- "libvndk",
- ],
- static_libs: [
- "libvendor",
- "libvendor_available",
- "libvndk",
- ],
- shared_libs: [
- "libvendor",
- "libvendor_available",
- ],
- binaries: [
- "bin",
- ],
+ arch: {
+ arm64: {
+ vndk_libs: [
+ "libvndk",
+ ],
+ static_libs: [
+ "libvendor",
+ "libvendor_available",
+ "libvndk",
+ "lib64",
+ ],
+ shared_libs: [
+ "libvendor",
+ "libvendor_available",
+ "lib64",
+ ],
+ binaries: [
+ "bin",
+ ],
+ },
+ arm: {
+ vndk_libs: [
+ "libvndk",
+ ],
+ static_libs: [
+ "libvendor",
+ "libvendor_available",
+ "libvndk",
+ "lib32",
+ ],
+ shared_libs: [
+ "libvendor",
+ "libvendor_available",
+ "lib32",
+ ],
+ binaries: [
+ "bin32",
+ ],
+ },
+ }
}
vendor_snapshot_static {
name: "libvndk",
version: "28",
target_arch: "arm64",
+ compile_multilib: "both",
vendor: true,
arch: {
arm64: {
src: "libvndk.a",
export_include_dirs: ["include/libvndk"],
},
+ arm: {
+ src: "libvndk.a",
+ export_include_dirs: ["include/libvndk"],
+ },
},
}
@@ -416,7 +502,7 @@
name: "libvendor",
version: "28",
target_arch: "arm64",
- compile_multilib: "64",
+ compile_multilib: "both",
vendor: true,
shared_libs: [
"libvendor_without_snapshot",
@@ -428,6 +514,62 @@
src: "libvendor.so",
export_include_dirs: ["include/libvendor"],
},
+ arm: {
+ src: "libvendor.so",
+ export_include_dirs: ["include/libvendor"],
+ },
+ },
+ }
+
+ vendor_snapshot_static {
+ name: "lib32",
+ version: "28",
+ target_arch: "arm64",
+ compile_multilib: "32",
+ vendor: true,
+ arch: {
+ arm: {
+ src: "lib32.a",
+ },
+ },
+ }
+
+ vendor_snapshot_shared {
+ name: "lib32",
+ version: "28",
+ target_arch: "arm64",
+ compile_multilib: "32",
+ vendor: true,
+ arch: {
+ arm: {
+ src: "lib32.so",
+ },
+ },
+ }
+
+ vendor_snapshot_static {
+ name: "lib64",
+ version: "28",
+ target_arch: "arm64",
+ compile_multilib: "64",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "lib64.a",
+ },
+ },
+ }
+
+ vendor_snapshot_shared {
+ name: "lib64",
+ version: "28",
+ target_arch: "arm64",
+ compile_multilib: "64",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "lib64.so",
+ },
},
}
@@ -435,40 +577,53 @@
name: "libvendor",
version: "28",
target_arch: "arm64",
+ compile_multilib: "both",
vendor: true,
arch: {
arm64: {
src: "libvendor.a",
export_include_dirs: ["include/libvendor"],
},
+ arm: {
+ src: "libvendor.a",
+ export_include_dirs: ["include/libvendor"],
+ },
},
}
vendor_snapshot_shared {
name: "libvendor_available",
- androidmk_suffix: ".vendor",
version: "28",
target_arch: "arm64",
+ compile_multilib: "both",
vendor: true,
arch: {
arm64: {
src: "libvendor_available.so",
export_include_dirs: ["include/libvendor"],
},
+ arm: {
+ src: "libvendor_available.so",
+ export_include_dirs: ["include/libvendor"],
+ },
},
}
vendor_snapshot_static {
name: "libvendor_available",
- androidmk_suffix: ".vendor",
version: "28",
target_arch: "arm64",
+ compile_multilib: "both",
vendor: true,
arch: {
arm64: {
src: "libvendor_available.a",
export_include_dirs: ["include/libvendor"],
},
+ arm: {
+ src: "libvendor_available.so",
+ export_include_dirs: ["include/libvendor"],
+ },
},
}
@@ -476,6 +631,7 @@
name: "bin",
version: "28",
target_arch: "arm64",
+ compile_multilib: "64",
vendor: true,
arch: {
arm64: {
@@ -484,11 +640,39 @@
},
}
+ vendor_snapshot_binary {
+ name: "bin32",
+ version: "28",
+ target_arch: "arm64",
+ compile_multilib: "32",
+ vendor: true,
+ arch: {
+ arm: {
+ src: "bin32",
+ },
+ },
+ }
+
// old snapshot module which has to be ignored
vendor_snapshot_binary {
name: "bin",
version: "26",
target_arch: "arm64",
+ compile_multilib: "first",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "bin",
+ },
+ },
+ }
+
+ // different arch snapshot which has to be ignored
+ vendor_snapshot_binary {
+ name: "bin",
+ version: "28",
+ target_arch: "arm",
+ compile_multilib: "first",
vendor: true,
arch: {
arm64: {
@@ -504,6 +688,7 @@
"framework/Android.bp": []byte(frameworkBp),
"vendor/Android.bp": []byte(vendorProprietaryBp),
"vendor/bin": nil,
+ "vendor/bin32": nil,
"vendor/bin.cpp": nil,
"vendor/client.cpp": nil,
"vendor/include/libvndk/a.h": nil,
@@ -511,6 +696,10 @@
"vendor/libvndk.a": nil,
"vendor/libvendor.a": nil,
"vendor/libvendor.so": nil,
+ "vendor/lib32.a": nil,
+ "vendor/lib32.so": nil,
+ "vendor/lib64.a": nil,
+ "vendor/lib64.so": nil,
"vndk/Android.bp": []byte(vndkBp),
"vndk/include/libvndk/a.h": nil,
"vndk/libvndk.so": nil,
@@ -531,6 +720,9 @@
staticVariant := "android_vendor.28_arm64_armv8-a_static"
binaryVariant := "android_vendor.28_arm64_armv8-a"
+ shared32Variant := "android_vendor.28_arm_armv7-a-neon_shared"
+ binary32Variant := "android_vendor.28_arm_armv7-a-neon"
+
// libclient uses libvndk.vndk.28.arm64, libvendor.vendor_static.28.arm64, libvendor_without_snapshot
libclientCcFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("cc").Args["cFlags"]
for _, includeFlags := range []string{
@@ -556,7 +748,7 @@
}
libclientAndroidMkSharedLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkSharedLibs
- if g, w := libclientAndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor"}; !reflect.DeepEqual(g, w) {
+ if g, w := libclientAndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "lib64"}; !reflect.DeepEqual(g, w) {
t.Errorf("wanted libclient AndroidMkSharedLibs %q, got %q", w, g)
}
@@ -565,6 +757,11 @@
t.Errorf("wanted libclient AndroidMkStaticLibs %q, got %q", w, g)
}
+ libclient32AndroidMkSharedLibs := ctx.ModuleForTests("libclient", shared32Variant).Module().(*Module).Properties.AndroidMkSharedLibs
+ if g, w := libclient32AndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "lib32"}; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted libclient32 AndroidMkSharedLibs %q, got %q", w, g)
+ }
+
// bin_without_snapshot uses libvndk.vendor_static.28.arm64
binWithoutSnapshotCcFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("cc").Args["cFlags"]
if !strings.Contains(binWithoutSnapshotCcFlags, "-Ivendor/include/libvndk") {
@@ -582,6 +779,12 @@
// libvendor.so is installed by libvendor.vendor_shared.28.arm64
ctx.ModuleForTests("libvendor.vendor_shared.28.arm64", sharedVariant).Output("libvendor.so")
+ // lib64.so is installed by lib64.vendor_shared.28.arm64
+ ctx.ModuleForTests("lib64.vendor_shared.28.arm64", sharedVariant).Output("lib64.so")
+
+ // lib32.so is installed by lib32.vendor_shared.28.arm64
+ ctx.ModuleForTests("lib32.vendor_shared.28.arm64", shared32Variant).Output("lib32.so")
+
// libvendor_available.so is installed by libvendor_available.vendor_shared.28.arm64
ctx.ModuleForTests("libvendor_available.vendor_shared.28.arm64", sharedVariant).Output("libvendor_available.so")
@@ -591,6 +794,9 @@
// bin is installed by bin.vendor_binary.28.arm64
ctx.ModuleForTests("bin.vendor_binary.28.arm64", binaryVariant).Output("bin")
+ // bin32 is installed by bin32.vendor_binary.28.arm64
+ ctx.ModuleForTests("bin32.vendor_binary.28.arm64", binary32Variant).Output("bin32")
+
// bin_without_snapshot is installed by bin_without_snapshot
ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Output("bin_without_snapshot")
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 6ad4ff3..7e9477b 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -186,17 +186,6 @@
// We do not have prebuilts of the core platform api yet
corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
- // Add the android.test.base to the set of stubs only if the android.test.base module is on
- // the boot jars list as the runtime will only enforce hiddenapi access against modules on
- // that list.
- if inList("android.test.base", ctx.Config().BootJars()) {
- if ctx.Config().AlwaysUsePrebuiltSdks() {
- publicStubModules = append(publicStubModules, "sdk_public_current_android.test.base")
- } else {
- publicStubModules = append(publicStubModules, "android.test.base.stubs")
- }
- }
-
// Allow products to define their own stubs for custom product jars that apps can use.
publicStubModules = append(publicStubModules, ctx.Config().ProductHiddenAPIStubs()...)
systemStubModules = append(systemStubModules, ctx.Config().ProductHiddenAPIStubsSystem()...)
@@ -369,20 +358,20 @@
FlagWithInput("--csv ", stubFlags).
Inputs(flagsCSV).
FlagWithInput("--unsupported ",
- android.PathForSource(ctx, "frameworks/base/config/hiddenapi-unsupported.txt")).
+ android.PathForSource(ctx, "frameworks/base/boot/hiddenapi/hiddenapi-unsupported.txt")).
FlagWithInput("--unsupported ", combinedRemovedApis).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed").
FlagWithInput("--max-target-r ",
- android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-r-loprio.txt")).FlagWithArg("--tag ", "lo-prio").
+ android.PathForSource(ctx, "frameworks/base/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt")).FlagWithArg("--tag ", "lo-prio").
FlagWithInput("--max-target-q ",
- android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-q.txt")).
+ android.PathForSource(ctx, "frameworks/base/boot/hiddenapi/hiddenapi-max-target-q.txt")).
FlagWithInput("--max-target-p ",
- android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-p.txt")).
+ android.PathForSource(ctx, "frameworks/base/boot/hiddenapi/hiddenapi-max-target-p.txt")).
FlagWithInput("--max-target-o ", android.PathForSource(
- ctx, "frameworks/base/config/hiddenapi-max-target-o.txt")).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio").
+ ctx, "frameworks/base/boot/hiddenapi/hiddenapi-max-target-o.txt")).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio").
FlagWithInput("--blocked ",
- android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blocked.txt")).
+ android.PathForSource(ctx, "frameworks/base/boot/hiddenapi/hiddenapi-force-blocked.txt")).
FlagWithInput("--unsupported ", android.PathForSource(
- ctx, "frameworks/base/config/hiddenapi-unsupported-packages.txt")).Flag("--packages ").
+ ctx, "frameworks/base/boot/hiddenapi/hiddenapi-unsupported-packages.txt")).Flag("--packages ").
FlagWithOutput("--output ", tempPath)
commitChangeForRestat(rule, tempPath, outputPath)
diff --git a/java/sdk_library.go b/java/sdk_library.go
index eb9ba9b..96135c3 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -201,8 +201,12 @@
return scope
}
+func (scope *apiScope) stubsLibraryModuleNameSuffix() string {
+ return ".stubs" + scope.moduleSuffix
+}
+
func (scope *apiScope) stubsLibraryModuleName(baseName string) string {
- return baseName + ".stubs" + scope.moduleSuffix
+ return baseName + scope.stubsLibraryModuleNameSuffix()
}
func (scope *apiScope) stubsSourceModuleName(baseName string) string {
@@ -1684,16 +1688,20 @@
func moduleStubLinkType(name string) (stub bool, ret sdkLinkType) {
// This suffix-based approach is fragile and could potentially mis-trigger.
// TODO(b/155164730): Clean this up when modules no longer reference sdk_lib stubs directly.
- if strings.HasSuffix(name, ".stubs.public") || strings.HasSuffix(name, "-stubs-publicapi") {
+ if strings.HasSuffix(name, apiScopePublic.stubsLibraryModuleNameSuffix()) {
+ if name == "hwbinder.stubs" || name == "libcore_private.stubs" {
+ // Due to a previous bug, these modules were not considered stubs, so we retain that.
+ return false, javaPlatform
+ }
return true, javaSdk
}
- if strings.HasSuffix(name, ".stubs.system") || strings.HasSuffix(name, "-stubs-systemapi") {
+ if strings.HasSuffix(name, apiScopeSystem.stubsLibraryModuleNameSuffix()) {
return true, javaSystem
}
- if strings.HasSuffix(name, ".stubs.module_lib") || strings.HasSuffix(name, "-stubs-module_libs_api") {
+ if strings.HasSuffix(name, apiScopeModuleLib.stubsLibraryModuleNameSuffix()) {
return true, javaModule
}
- if strings.HasSuffix(name, ".stubs.test") {
+ if strings.HasSuffix(name, apiScopeTest.stubsLibraryModuleNameSuffix()) {
return true, javaSystem
}
return false, javaPlatform
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 0f9a17d..b0e6967 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -50,7 +50,7 @@
}
ret := android.AndroidMkEntries{
- OutputFile: mod.outputFile,
+ OutputFile: mod.unstrippedOutputFile,
Include: "$(BUILD_SYSTEM)/soong_rust_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
diff --git a/rust/compiler.go b/rust/compiler.go
index 41b7371..aaa1924 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -76,7 +76,7 @@
// errors). The default value is "default".
Lints *string
- // flags to pass to rustc
+ // flags to pass to rustc. To enable configuration options or features, use the "cfgs" or "features" properties.
Flags []string `android:"path,arch_variant"`
// flags to pass to the linker
@@ -125,6 +125,9 @@
// list of features to enable for this crate
Features []string `android:"arch_variant"`
+ // list of configuration options to enable for this crate. To enable features, use the "features" property.
+ Cfgs []string `android:"arch_variant"`
+
// specific rust edition that should be used if the default version is not desired
Edition *string `android:"arch_variant"`
@@ -210,9 +213,17 @@
return []interface{}{&compiler.Properties}
}
-func (compiler *baseCompiler) featuresToFlags(features []string) []string {
+func (compiler *baseCompiler) cfgsToFlags() []string {
flags := []string{}
- for _, feature := range features {
+ for _, cfg := range compiler.Properties.Cfgs {
+ flags = append(flags, "--cfg '"+cfg+"'")
+ }
+ return flags
+}
+
+func (compiler *baseCompiler) featuresToFlags() []string {
+ flags := []string{}
+ for _, feature := range compiler.Properties.Features {
flags = append(flags, "--cfg 'feature=\""+feature+"\"'")
}
return flags
@@ -226,7 +237,8 @@
}
flags.RustFlags = append(flags.RustFlags, lintFlags)
flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...)
- flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags(compiler.Properties.Features)...)
+ flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...)
+ flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags()...)
flags.RustFlags = append(flags.RustFlags, "--edition="+compiler.edition())
flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...)
flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...)
@@ -272,6 +284,10 @@
return false
}
+func (compiler *baseCompiler) strippedOutputFilePath() android.OptionalPath {
+ return compiler.strippedOutputFile
+}
+
func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs...)
deps.Dylibs = append(deps.Dylibs, compiler.Properties.Dylibs...)
@@ -337,10 +353,7 @@
}
func (compiler *baseCompiler) install(ctx ModuleContext) {
- path := ctx.RustModule().outputFile
- if compiler.strippedOutputFile.Valid() {
- path = compiler.strippedOutputFile
- }
+ path := ctx.RustModule().OutputFile()
compiler.path = ctx.InstallFile(compiler.installDir(ctx), path.Path().Base(), path.Path())
}
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index c752762..5ca9e7f 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -42,6 +42,27 @@
}
}
+// Test that cfgs flags are being correctly generated.
+func TestCfgsToFlags(t *testing.T) {
+ ctx := testRust(t, `
+ rust_library_host {
+ name: "libfoo",
+ srcs: ["foo.rs"],
+ crate_name: "foo",
+ cfgs: [
+ "std",
+ "cfg1=\"one\""
+ ],
+ }`)
+
+ libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+
+ if !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'std'") ||
+ !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'cfg1=\"one\"'") {
+ t.Fatalf("missing std and cfg1 flags for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
+ }
+}
+
// Test that we reject multiple source files.
func TestEnforceSingleSourceFile(t *testing.T) {
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 1c8e43e..394fcc5 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -23,6 +23,7 @@
"system/extras/profcollectd",
"system/extras/simpleperf",
"system/hardware/interfaces/keystore2",
+ "system/logging/rust",
"system/security",
"system/tools/aidl",
}
diff --git a/rust/rust.go b/rust/rust.go
index f0d0e36..566ad37 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -114,7 +114,10 @@
sourceProvider SourceProvider
subAndroidMkOnce map[SubAndroidMkProvider]bool
- outputFile android.OptionalPath
+ // Unstripped output. This is usually used when this module is linked to another module
+ // as a library. The stripped output which is used for installation can be found via
+ // compiler.strippedOutputFile if it exists.
+ unstrippedOutputFile android.OptionalPath
hideApexVariantFromMake bool
}
@@ -163,8 +166,8 @@
if mod.sourceProvider != nil && (mod.compiler == nil || mod.compiler.Disabled()) {
return mod.sourceProvider.Srcs(), nil
} else {
- if mod.outputFile.Valid() {
- return android.Paths{mod.outputFile.Path()}, nil
+ if mod.OutputFile().Valid() {
+ return android.Paths{mod.OutputFile().Path()}, nil
}
return android.Paths{}, nil
}
@@ -346,6 +349,8 @@
stdLinkage(ctx *depsContext) RustLinkage
isDependencyRoot() bool
+
+ strippedOutputFilePath() android.OptionalPath
}
type exportedFlagsProducer interface {
@@ -523,7 +528,10 @@
}
func (mod *Module) OutputFile() android.OptionalPath {
- return mod.outputFile
+ if mod.compiler != nil && mod.compiler.strippedOutputFilePath().Valid() {
+ return mod.compiler.strippedOutputFilePath()
+ }
+ return mod.unstrippedOutputFile
}
func (mod *Module) CoverageFiles() android.Paths {
@@ -540,7 +548,7 @@
return false
}
- return mod.outputFile.Valid() && !mod.Properties.PreventInstall
+ return mod.OutputFile().Valid() && !mod.Properties.PreventInstall
}
var _ cc.LinkableInterface = (*Module)(nil)
@@ -721,9 +729,9 @@
if mod.compiler != nil && !mod.compiler.Disabled() {
mod.compiler.initialize(ctx)
- outputFile := mod.compiler.compile(ctx, flags, deps)
+ unstrippedOutputFile := mod.compiler.compile(ctx, flags, deps)
- mod.outputFile = android.OptionalPathForPath(outputFile)
+ mod.unstrippedOutputFile = android.OptionalPathForPath(unstrippedOutputFile)
apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
if mod.installable(apexInfo) {
@@ -882,7 +890,7 @@
}
if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
- linkFile := rustDep.outputFile
+ linkFile := rustDep.unstrippedOutputFile
if !linkFile.Valid() {
ctx.ModuleErrorf("Invalid output file when adding dep %q to %q",
depName, ctx.ModuleName())
@@ -978,15 +986,15 @@
var rlibDepFiles RustLibraries
for _, dep := range directRlibDeps {
- rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.outputFile.Path(), CrateName: dep.CrateName()})
+ rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.unstrippedOutputFile.Path(), CrateName: dep.CrateName()})
}
var dylibDepFiles RustLibraries
for _, dep := range directDylibDeps {
- dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.outputFile.Path(), CrateName: dep.CrateName()})
+ dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.unstrippedOutputFile.Path(), CrateName: dep.CrateName()})
}
var procMacroDepFiles RustLibraries
for _, dep := range directProcMacroDeps {
- procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.outputFile.Path(), CrateName: dep.CrateName()})
+ procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.unstrippedOutputFile.Path(), CrateName: dep.CrateName()})
}
var staticLibDepFiles android.Paths
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 9e8a602..c54b2bc 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -254,3 +254,22 @@
"linker_config_proto",
],
}
+
+python_binary_host {
+ name: "conv_classpaths_proto",
+ srcs: [
+ "conv_classpaths_proto.py",
+ ],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ },
+ libs: [
+ "classpaths_proto_python",
+ ],
+}
diff --git a/scripts/conv_classpaths_proto.py b/scripts/conv_classpaths_proto.py
new file mode 100644
index 0000000..f49fbbb
--- /dev/null
+++ b/scripts/conv_classpaths_proto.py
@@ -0,0 +1,76 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+
+import classpaths_pb2
+
+import google.protobuf.json_format as json_format
+import google.protobuf.text_format as text_format
+
+
+def encode(args):
+ pb = classpaths_pb2.ExportedClasspathsJars()
+ if args.format == 'json':
+ json_format.Parse(args.input.read(), pb)
+ else:
+ text_format.Parse(args.input.read(), pb)
+ args.output.write(pb.SerializeToString())
+ args.input.close()
+ args.output.close()
+
+
+def decode(args):
+ pb = classpaths_pb2.ExportedClasspathsJars()
+ pb.ParseFromString(args.input.read())
+ if args.format == 'json':
+ args.output.write(json_format.MessageToJson(pb))
+ else:
+ args.output.write(text_format.MessageToString(pb).encode('utf_8'))
+ args.input.close()
+ args.output.close()
+
+
+def main():
+ parser = argparse.ArgumentParser('Convert classpaths.proto messages between binary and '
+ 'human-readable formats.')
+ parser.add_argument('-f', '--format', default='textproto',
+ help='human-readable format, either json or text(proto), '
+ 'defaults to textproto')
+ parser.add_argument('-i', '--input',
+ nargs='?', type=argparse.FileType('rb'), default=sys.stdin.buffer)
+ parser.add_argument('-o', '--output',
+ nargs='?', type=argparse.FileType('wb'),
+ default=sys.stdout.buffer)
+
+ subparsers = parser.add_subparsers()
+
+ parser_encode = subparsers.add_parser('encode',
+ help='convert classpaths protobuf message from '
+ 'JSON to binary format',
+ parents=[parser], add_help=False)
+
+ parser_encode.set_defaults(func=encode)
+
+ parser_decode = subparsers.add_parser('decode',
+ help='print classpaths config in JSON format',
+ parents=[parser], add_help=False)
+ parser_decode.set_defaults(func=decode)
+
+ args = parser.parse_args()
+ args.func(args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/bootstrap_test.sh b/tests/bootstrap_test.sh
similarity index 77%
rename from bootstrap_test.sh
rename to tests/bootstrap_test.sh
index 9d87697..b2f7b2b 100755
--- a/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -3,106 +3,13 @@
# This test exercises the bootstrapping process of the build system
# in a source tree that only contains enough files for Bazel and Soong to work.
-HARDWIRED_MOCK_TOP=
-# Uncomment this to be able to view the source tree after a test is run
-# HARDWIRED_MOCK_TOP=/tmp/td
-
-REAL_TOP="$(readlink -f "$(dirname "$0")"/../..)"
-
-function fail {
- echo ERROR: $1
- exit 1
-}
-
-function copy_directory() {
- local dir="$1"
- local parent="$(dirname "$dir")"
-
- mkdir -p "$MOCK_TOP/$parent"
- cp -R "$REAL_TOP/$dir" "$MOCK_TOP/$parent"
-}
-
-function symlink_file() {
- local file="$1"
-
- mkdir -p "$MOCK_TOP/$(dirname "$file")"
- ln -s "$REAL_TOP/$file" "$MOCK_TOP/$file"
-}
-
-function symlink_directory() {
- local dir="$1"
-
- mkdir -p "$MOCK_TOP/$dir"
- # We need to symlink the contents of the directory individually instead of
- # using one symlink for the whole directory because finder.go doesn't follow
- # symlinks when looking for Android.bp files
- for i in $(ls "$REAL_TOP/$dir"); do
- local target="$MOCK_TOP/$dir/$i"
- local source="$REAL_TOP/$dir/$i"
-
- if [[ -e "$target" ]]; then
- if [[ ! -d "$source" || ! -d "$target" ]]; then
- fail "Trying to symlink $dir twice"
- fi
- else
- ln -s "$REAL_TOP/$dir/$i" "$MOCK_TOP/$dir/$i";
- fi
- done
-}
-
-function setup_bazel() {
- copy_directory build/bazel
-
- symlink_directory prebuilts/bazel
- symlink_directory prebuilts/jdk
-
- symlink_file WORKSPACE
- symlink_file tools/bazel
-}
-
-function setup() {
- if [[ ! -z "$HARDWIRED_MOCK_TOP" ]]; then
- MOCK_TOP="$HARDWIRED_MOCK_TOP"
- rm -fr "$MOCK_TOP"
- mkdir -p "$MOCK_TOP"
- else
- MOCK_TOP=$(mktemp -t -d st.XXXXX)
- trap 'echo cd / && echo rm -fr "$MOCK_TOP"' EXIT
- fi
-
- echo "Test case: ${FUNCNAME[1]}, mock top path: $MOCK_TOP"
- cd "$MOCK_TOP"
-
- copy_directory build/blueprint
- copy_directory build/soong
-
- symlink_directory prebuilts/go
- symlink_directory prebuilts/build-tools
- symlink_directory external/golang-protobuf
-
- touch "$MOCK_TOP/Android.bp"
-
- export ALLOW_MISSING_DEPENDENCIES=true
-
- mkdir -p out/soong
-}
-
-function run_soong() {
- build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests
-}
+source "$(dirname "$0")/lib.sh"
function test_smoke {
setup
run_soong
}
-function test_bazel_smoke {
- setup
- setup_bazel
-
- tools/bazel info
-
-}
function test_null_build() {
setup
run_soong
@@ -410,7 +317,6 @@
fi
}
-test_bazel_smoke
test_smoke
test_null_build
test_null_build_after_docs
diff --git a/tests/lib.sh b/tests/lib.sh
new file mode 100644
index 0000000..3c97e14
--- /dev/null
+++ b/tests/lib.sh
@@ -0,0 +1,79 @@
+#!/bin/bash -eu
+
+HARDWIRED_MOCK_TOP=
+# Uncomment this to be able to view the source tree after a test is run
+# HARDWIRED_MOCK_TOP=/tmp/td
+
+REAL_TOP="$(readlink -f "$(dirname "$0")"/../../..)"
+
+function fail {
+ echo ERROR: $1
+ exit 1
+}
+
+function copy_directory() {
+ local dir="$1"
+ local parent="$(dirname "$dir")"
+
+ mkdir -p "$MOCK_TOP/$parent"
+ cp -R "$REAL_TOP/$dir" "$MOCK_TOP/$parent"
+}
+
+function symlink_file() {
+ local file="$1"
+
+ mkdir -p "$MOCK_TOP/$(dirname "$file")"
+ ln -s "$REAL_TOP/$file" "$MOCK_TOP/$file"
+}
+
+function symlink_directory() {
+ local dir="$1"
+
+ mkdir -p "$MOCK_TOP/$dir"
+ # We need to symlink the contents of the directory individually instead of
+ # using one symlink for the whole directory because finder.go doesn't follow
+ # symlinks when looking for Android.bp files
+ for i in $(ls "$REAL_TOP/$dir"); do
+ local target="$MOCK_TOP/$dir/$i"
+ local source="$REAL_TOP/$dir/$i"
+
+ if [[ -e "$target" ]]; then
+ if [[ ! -d "$source" || ! -d "$target" ]]; then
+ fail "Trying to symlink $dir twice"
+ fi
+ else
+ ln -s "$REAL_TOP/$dir/$i" "$MOCK_TOP/$dir/$i";
+ fi
+ done
+}
+
+function setup() {
+ if [[ ! -z "$HARDWIRED_MOCK_TOP" ]]; then
+ MOCK_TOP="$HARDWIRED_MOCK_TOP"
+ rm -fr "$MOCK_TOP"
+ mkdir -p "$MOCK_TOP"
+ else
+ MOCK_TOP=$(mktemp -t -d st.XXXXX)
+ trap 'echo cd / && echo rm -fr "$MOCK_TOP"' EXIT
+ fi
+
+ echo "Test case: ${FUNCNAME[1]}, mock top path: $MOCK_TOP"
+ cd "$MOCK_TOP"
+
+ copy_directory build/blueprint
+ copy_directory build/soong
+
+ symlink_directory prebuilts/go
+ symlink_directory prebuilts/build-tools
+ symlink_directory external/golang-protobuf
+
+ touch "$MOCK_TOP/Android.bp"
+
+ export ALLOW_MISSING_DEPENDENCIES=true
+
+ mkdir -p out/soong
+}
+
+function run_soong() {
+ build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests
+}
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
new file mode 100755
index 0000000..54f0689
--- /dev/null
+++ b/tests/mixed_mode_test.sh
@@ -0,0 +1,28 @@
+#!/bin/bash -eu
+
+# This test exercises mixed builds where Soong and Bazel cooperate in building
+# Android.
+#
+# When the execroot is deleted, the Bazel server process will automatically
+# terminate itself.
+
+source "$(dirname "$0")/lib.sh"
+
+function setup_bazel() {
+ copy_directory build/bazel
+
+ symlink_directory prebuilts/bazel
+ symlink_directory prebuilts/jdk
+
+ symlink_file WORKSPACE
+ symlink_file tools/bazel
+}
+
+function test_bazel_smoke {
+ setup
+ setup_bazel
+
+ tools/bazel info
+}
+
+test_bazel_smoke
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
new file mode 100755
index 0000000..db24037
--- /dev/null
+++ b/tests/run_integration_tests.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+TOP="$(readlink -f "$(dirname "$0")"/../../..)"
+"$TOP/build/soong/tests/bootstrap_test.sh"
+"$TOP/build/soong/tests/mixed_mode_test.sh"
+