summaryrefslogtreecommitdiff
path: root/sdk
diff options
context:
space:
mode:
Diffstat (limited to 'sdk')
-rw-r--r--sdk/Android.bp27
-rw-r--r--sdk/bp.go301
-rw-r--r--sdk/bp_test.go76
-rw-r--r--sdk/cc_sdk_test.go1893
-rw-r--r--sdk/exports.go36
-rw-r--r--sdk/exports_test.go66
-rw-r--r--sdk/java_sdk_test.go1560
-rw-r--r--sdk/sdk.go468
-rw-r--r--sdk/sdk_test.go410
-rw-r--r--sdk/testing.go429
-rw-r--r--sdk/update.go1494
11 files changed, 6760 insertions, 0 deletions
diff --git a/sdk/Android.bp b/sdk/Android.bp
new file mode 100644
index 000000000..cb93351d9
--- /dev/null
+++ b/sdk/Android.bp
@@ -0,0 +1,27 @@
+bootstrap_go_package {
+ name: "soong-sdk",
+ pkgPath: "android/soong/sdk",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ "soong-apex",
+ "soong-cc",
+ "soong-java",
+ ],
+ srcs: [
+ "bp.go",
+ "exports.go",
+ "sdk.go",
+ "update.go",
+ ],
+ testSrcs: [
+ "bp_test.go",
+ "cc_sdk_test.go",
+ "exports_test.go",
+ "java_sdk_test.go",
+ "sdk_test.go",
+ "testing.go",
+ ],
+ pluginFor: ["soong_build"],
+}
diff --git a/sdk/bp.go b/sdk/bp.go
new file mode 100644
index 000000000..68fe7ab4e
--- /dev/null
+++ b/sdk/bp.go
@@ -0,0 +1,301 @@
+// Copyright (C) 2019 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.
+
+package sdk
+
+import (
+ "fmt"
+
+ "android/soong/android"
+)
+
+type bpPropertySet struct {
+ properties map[string]interface{}
+ tags map[string]android.BpPropertyTag
+ order []string
+}
+
+var _ android.BpPropertySet = (*bpPropertySet)(nil)
+
+func (s *bpPropertySet) init() {
+ s.properties = make(map[string]interface{})
+ s.tags = make(map[string]android.BpPropertyTag)
+}
+
+func (s *bpPropertySet) AddProperty(name string, value interface{}) {
+ if s.properties[name] != nil {
+ panic(fmt.Sprintf("Property %q already exists in property set", name))
+ }
+
+ s.properties[name] = value
+ s.order = append(s.order, name)
+}
+
+func (s *bpPropertySet) AddPropertyWithTag(name string, value interface{}, tag android.BpPropertyTag) {
+ s.AddProperty(name, value)
+ s.tags[name] = tag
+}
+
+func (s *bpPropertySet) AddPropertySet(name string) android.BpPropertySet {
+ set := newPropertySet()
+ s.AddProperty(name, set)
+ return set
+}
+
+func (s *bpPropertySet) getValue(name string) interface{} {
+ return s.properties[name]
+}
+
+func (s *bpPropertySet) getTag(name string) interface{} {
+ return s.tags[name]
+}
+
+func (s *bpPropertySet) transformContents(transformer bpPropertyTransformer) {
+ var newOrder []string
+ for _, name := range s.order {
+ value := s.properties[name]
+ tag := s.tags[name]
+ var newValue interface{}
+ var newTag android.BpPropertyTag
+ if propertySet, ok := value.(*bpPropertySet); ok {
+ var newPropertySet *bpPropertySet
+ newPropertySet, newTag = transformPropertySet(transformer, name, propertySet, tag)
+ if newPropertySet == nil {
+ newValue = nil
+ } else {
+ newValue = newPropertySet
+ }
+ } else {
+ newValue, newTag = transformer.transformProperty(name, value, tag)
+ }
+
+ if newValue == nil {
+ // Delete the property from the map and exclude it from the new order.
+ delete(s.properties, name)
+ } else {
+ // Update the property in the map and add the name to the new order list.
+ s.properties[name] = newValue
+ s.tags[name] = newTag
+ newOrder = append(newOrder, name)
+ }
+ }
+ s.order = newOrder
+}
+
+func transformPropertySet(transformer bpPropertyTransformer, name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+ newPropertySet, newTag := transformer.transformPropertySetBeforeContents(name, propertySet, tag)
+ if newPropertySet != nil {
+ newPropertySet.transformContents(transformer)
+
+ newPropertySet, newTag = transformer.transformPropertySetAfterContents(name, newPropertySet, newTag)
+ }
+ return newPropertySet, newTag
+}
+
+func (s *bpPropertySet) setProperty(name string, value interface{}) {
+ if s.properties[name] == nil {
+ s.AddProperty(name, value)
+ } else {
+ s.properties[name] = value
+ s.tags[name] = nil
+ }
+}
+
+func (s *bpPropertySet) insertAfter(position string, name string, value interface{}) {
+ if s.properties[name] != nil {
+ panic("Property %q already exists in property set")
+ }
+
+ // Add the name to the end of the order, to ensure it has necessary capacity
+ // and to handle the case when the position does not exist.
+ s.order = append(s.order, name)
+
+ // Search through the order for the item that matches supplied position. If
+ // found then insert the name of the new property after it.
+ for i, v := range s.order {
+ if v == position {
+ // Copy the items after the one where the new property should be inserted.
+ copy(s.order[i+2:], s.order[i+1:])
+ // Insert the item in the list.
+ s.order[i+1] = name
+ }
+ }
+
+ s.properties[name] = value
+}
+
+type bpModule struct {
+ *bpPropertySet
+ moduleType string
+}
+
+var _ android.BpModule = (*bpModule)(nil)
+
+type bpPropertyTransformer interface {
+ // Transform the property set, returning the new property set/tag to insert back into the
+ // parent property set (or module if this is the top level property set).
+ //
+ // This will be called before transforming the properties in the supplied set.
+ //
+ // The name will be "" for the top level property set.
+ //
+ // Returning (nil, ...) will cause the property set to be removed.
+ transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag)
+
+ // Transform the property set, returning the new property set/tag to insert back into the
+ // parent property set (or module if this is the top level property set).
+ //
+ // This will be called after transforming the properties in the supplied set.
+ //
+ // The name will be "" for the top level property set.
+ //
+ // Returning (nil, ...) will cause the property set to be removed.
+ transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag)
+
+ // Transform a property, return the new value/tag to insert back into the property set.
+ //
+ // Returning (nil, ...) will cause the property to be removed.
+ transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag)
+}
+
+// Interface for transforming bpModule objects.
+type bpTransformer interface {
+ // Transform the module, returning the result.
+ //
+ // The method can either create a new module and return that, or modify the supplied module
+ // in place and return that.
+ //
+ // After this returns the transformer is applied to the contents of the returned module.
+ transformModule(module *bpModule) *bpModule
+
+ bpPropertyTransformer
+}
+
+type identityTransformation struct{}
+
+var _ bpTransformer = (*identityTransformation)(nil)
+
+func (t identityTransformation) transformModule(module *bpModule) *bpModule {
+ return module
+}
+
+func (t identityTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+ return propertySet, tag
+}
+
+func (t identityTransformation) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+ return propertySet, tag
+}
+
+func (t identityTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+ return value, tag
+}
+
+func (m *bpModule) deepCopy() *bpModule {
+ return m.transform(deepCopyTransformer)
+}
+
+func (m *bpModule) transform(transformer bpTransformer) *bpModule {
+ transformedModule := transformer.transformModule(m)
+ // Copy the contents of the returned property set into the module and then transform that.
+ transformedModule.bpPropertySet, _ = transformPropertySet(transformer, "", transformedModule.bpPropertySet, nil)
+ return transformedModule
+}
+
+type deepCopyTransformation struct {
+ identityTransformation
+}
+
+func (t deepCopyTransformation) transformModule(module *bpModule) *bpModule {
+ // Take a shallow copy of the module. Any mutable property values will be copied by the
+ // transformer.
+ moduleCopy := *module
+ return &moduleCopy
+}
+
+func (t deepCopyTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+ // Create a shallow copy of the properties map. Any mutable property values will be copied by the
+ // transformer.
+ propertiesCopy := make(map[string]interface{})
+ for propertyName, value := range propertySet.properties {
+ propertiesCopy[propertyName] = value
+ }
+
+ // Ditto for tags map.
+ tagsCopy := make(map[string]android.BpPropertyTag)
+ for propertyName, propertyTag := range propertySet.tags {
+ tagsCopy[propertyName] = propertyTag
+ }
+
+ // Create a new property set.
+ return &bpPropertySet{
+ properties: propertiesCopy,
+ tags: tagsCopy,
+ order: append([]string(nil), propertySet.order...),
+ }, tag
+}
+
+func (t deepCopyTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+ // Copy string slice, otherwise return value.
+ if values, ok := value.([]string); ok {
+ valuesCopy := make([]string, len(values))
+ copy(valuesCopy, values)
+ return valuesCopy, tag
+ }
+ return value, tag
+}
+
+var deepCopyTransformer bpTransformer = deepCopyTransformation{}
+
+// A .bp file
+type bpFile struct {
+ modules map[string]*bpModule
+ order []*bpModule
+}
+
+// Add a module.
+//
+// The module must have had its "name" property set to a string value that
+// is unique within this file.
+func (f *bpFile) AddModule(module android.BpModule) {
+ m := module.(*bpModule)
+ if name, ok := m.getValue("name").(string); ok {
+ if f.modules[name] != nil {
+ panic(fmt.Sprintf("Module %q already exists in bp file", name))
+ }
+
+ f.modules[name] = m
+ f.order = append(f.order, m)
+ } else {
+ panic("Module does not have a name property, or it is not a string")
+ }
+}
+
+func (f *bpFile) newModule(moduleType string) *bpModule {
+ return newModule(moduleType)
+}
+
+func newModule(moduleType string) *bpModule {
+ module := &bpModule{
+ moduleType: moduleType,
+ bpPropertySet: newPropertySet(),
+ }
+ return module
+}
+
+func newPropertySet() *bpPropertySet {
+ set := &bpPropertySet{}
+ set.init()
+ return set
+}
diff --git a/sdk/bp_test.go b/sdk/bp_test.go
new file mode 100644
index 000000000..c630c2524
--- /dev/null
+++ b/sdk/bp_test.go
@@ -0,0 +1,76 @@
+// Copyright (C) 2020 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.
+
+package sdk
+
+import (
+ "testing"
+
+ "android/soong/android"
+)
+
+type removeFredTransformation struct {
+ identityTransformation
+}
+
+func (t removeFredTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+ if name == "fred" {
+ return nil, nil
+ }
+ return value, tag
+}
+
+func (t removeFredTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+ if name == "fred" {
+ return nil, nil
+ }
+ return propertySet, tag
+}
+
+func (t removeFredTransformation) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+ if len(propertySet.properties) == 0 {
+ return nil, nil
+ }
+ return propertySet, tag
+}
+
+func TestTransformRemoveProperty(t *testing.T) {
+
+ helper := &TestHelper{t}
+
+ set := newPropertySet()
+ set.AddProperty("name", "name")
+ set.AddProperty("fred", "12")
+
+ set.transformContents(removeFredTransformation{})
+
+ contents := &generatedContents{}
+ outputPropertySet(contents, set)
+ helper.AssertTrimmedStringEquals("removing property failed", "name: \"name\",\n", contents.content.String())
+}
+
+func TestTransformRemovePropertySet(t *testing.T) {
+
+ helper := &TestHelper{t}
+
+ set := newPropertySet()
+ set.AddProperty("name", "name")
+ set.AddPropertySet("fred")
+
+ set.transformContents(removeFredTransformation{})
+
+ contents := &generatedContents{}
+ outputPropertySet(contents, set)
+ helper.AssertTrimmedStringEquals("removing property set failed", "name: \"name\",\n", contents.content.String())
+}
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
new file mode 100644
index 000000000..dded15360
--- /dev/null
+++ b/sdk/cc_sdk_test.go
@@ -0,0 +1,1893 @@
+// Copyright (C) 2019 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.
+
+package sdk
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/cc"
+)
+
+func testSdkWithCc(t *testing.T, bp string) *testSdkResult {
+ t.Helper()
+
+ fs := map[string][]byte{
+ "Test.cpp": nil,
+ "include/Test.h": nil,
+ "include-android/AndroidTest.h": nil,
+ "include-host/HostTest.h": nil,
+ "arm64/include/Arm64Test.h": nil,
+ "libfoo.so": nil,
+ "aidl/foo/bar/Test.aidl": nil,
+ "some/where/stubslib.map.txt": nil,
+ }
+ return testSdkWithFs(t, bp, fs)
+}
+
+// Contains tests for SDK members provided by the cc package.
+
+func TestSdkIsCompileMultilibBoth(t *testing.T) {
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ native_shared_libs: ["sdkmember"],
+ }
+
+ cc_library_shared {
+ name: "sdkmember",
+ srcs: ["Test.cpp"],
+ stl: "none",
+ }
+ `)
+
+ armOutput := result.Module("sdkmember", "android_arm_armv7-a-neon_shared").(*cc.Module).OutputFile()
+ arm64Output := result.Module("sdkmember", "android_arm64_armv8-a_shared").(*cc.Module).OutputFile()
+
+ var inputs []string
+ buildParams := result.Module("mysdk", android.CommonOS.Name).BuildParamsForTests()
+ for _, bp := range buildParams {
+ if bp.Input != nil {
+ inputs = append(inputs, bp.Input.String())
+ }
+ }
+
+ // ensure that both 32/64 outputs are inputs of the sdk snapshot
+ ensureListContains(t, inputs, armOutput.String())
+ ensureListContains(t, inputs, arm64Output.String())
+}
+
+func TestBasicSdkWithCc(t *testing.T) {
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ native_shared_libs: ["sdkmember"],
+ }
+
+ cc_library_shared {
+ name: "sdkmember",
+ system_shared_libs: [],
+ }
+
+ sdk_snapshot {
+ name: "mysdk@1",
+ native_shared_libs: ["sdkmember_mysdk_1"],
+ }
+
+ sdk_snapshot {
+ name: "mysdk@2",
+ native_shared_libs: ["sdkmember_mysdk_2"],
+ }
+
+ cc_prebuilt_library_shared {
+ name: "sdkmember",
+ srcs: ["libfoo.so"],
+ prefer: false,
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_prebuilt_library_shared {
+ name: "sdkmember_mysdk_1",
+ sdk_member_name: "sdkmember",
+ srcs: ["libfoo.so"],
+ system_shared_libs: [],
+ stl: "none",
+ // TODO: remove //apex_available:platform
+ apex_available: [
+ "//apex_available:platform",
+ "myapex",
+ ],
+ }
+
+ cc_prebuilt_library_shared {
+ name: "sdkmember_mysdk_2",
+ sdk_member_name: "sdkmember",
+ srcs: ["libfoo.so"],
+ system_shared_libs: [],
+ stl: "none",
+ // TODO: remove //apex_available:platform
+ apex_available: [
+ "//apex_available:platform",
+ "myapex2",
+ ],
+ }
+
+ cc_library_shared {
+ name: "mycpplib",
+ srcs: ["Test.cpp"],
+ shared_libs: ["sdkmember"],
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: [
+ "myapex",
+ "myapex2",
+ ],
+ }
+
+ apex {
+ name: "myapex",
+ native_shared_libs: ["mycpplib"],
+ uses_sdks: ["mysdk@1"],
+ key: "myapex.key",
+ certificate: ":myapex.cert",
+ }
+
+ apex {
+ name: "myapex2",
+ native_shared_libs: ["mycpplib"],
+ uses_sdks: ["mysdk@2"],
+ key: "myapex.key",
+ certificate: ":myapex.cert",
+ }
+ `)
+
+ sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk_1", "android_arm64_armv8-a_shared_myapex").Rule("toc").Output
+ sdkMemberV2 := result.ModuleForTests("sdkmember_mysdk_2", "android_arm64_armv8-a_shared_myapex2").Rule("toc").Output
+
+ cpplibForMyApex := result.ModuleForTests("mycpplib", "android_arm64_armv8-a_shared_myapex")
+ cpplibForMyApex2 := result.ModuleForTests("mycpplib", "android_arm64_armv8-a_shared_myapex2")
+
+ // Depending on the uses_sdks value, different libs are linked
+ ensureListContains(t, pathsToStrings(cpplibForMyApex.Rule("ld").Implicits), sdkMemberV1.String())
+ ensureListContains(t, pathsToStrings(cpplibForMyApex2.Rule("ld").Implicits), sdkMemberV2.String())
+}
+
+// Make sure the sdk can use host specific cc libraries static/shared and both.
+func TestHostSdkWithCc(t *testing.T) {
+ testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ device_supported: false,
+ host_supported: true,
+ native_shared_libs: ["sdkshared"],
+ native_static_libs: ["sdkstatic"],
+ }
+
+ cc_library_host_shared {
+ name: "sdkshared",
+ stl: "none",
+ }
+
+ cc_library_host_static {
+ name: "sdkstatic",
+ stl: "none",
+ }
+ `)
+}
+
+// Make sure the sdk can use cc libraries static/shared and both.
+func TestSdkWithCc(t *testing.T) {
+ testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ native_shared_libs: ["sdkshared", "sdkboth1"],
+ native_static_libs: ["sdkstatic", "sdkboth2"],
+ }
+
+ cc_library_shared {
+ name: "sdkshared",
+ stl: "none",
+ }
+
+ cc_library_static {
+ name: "sdkstatic",
+ stl: "none",
+ }
+
+ cc_library {
+ name: "sdkboth1",
+ stl: "none",
+ }
+
+ cc_library {
+ name: "sdkboth2",
+ stl: "none",
+ }
+ `)
+}
+
+func TestSnapshotWithObject(t *testing.T) {
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ native_objects: ["crtobj"],
+ }
+
+ cc_object {
+ name: "crtobj",
+ stl: "none",
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_object {
+ name: "mysdk_crtobj@current",
+ sdk_member_name: "crtobj",
+ stl: "none",
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/crtobj.o"],
+ },
+ arm: {
+ srcs: ["arm/lib/crtobj.o"],
+ },
+ },
+}
+
+cc_prebuilt_object {
+ name: "crtobj",
+ prefer: false,
+ stl: "none",
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/crtobj.o"],
+ },
+ arm: {
+ srcs: ["arm/lib/crtobj.o"],
+ },
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ native_objects: ["mysdk_crtobj@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/crtobj/android_arm64_armv8-a/crtobj.o -> arm64/lib/crtobj.o
+.intermediates/crtobj/android_arm_armv7-a-neon/crtobj.o -> arm/lib/crtobj.o
+`),
+ )
+}
+
+func TestSnapshotWithCcDuplicateHeaders(t *testing.T) {
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ native_shared_libs: ["mynativelib1", "mynativelib2"],
+ }
+
+ cc_library_shared {
+ name: "mynativelib1",
+ srcs: [
+ "Test.cpp",
+ ],
+ export_include_dirs: ["include"],
+ stl: "none",
+ }
+
+ cc_library_shared {
+ name: "mynativelib2",
+ srcs: [
+ "Test.cpp",
+ ],
+ export_include_dirs: ["include"],
+ stl: "none",
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib1/android_arm64_armv8-a_shared/mynativelib1.so -> arm64/lib/mynativelib1.so
+.intermediates/mynativelib1/android_arm_armv7-a-neon_shared/mynativelib1.so -> arm/lib/mynativelib1.so
+.intermediates/mynativelib2/android_arm64_armv8-a_shared/mynativelib2.so -> arm64/lib/mynativelib2.so
+.intermediates/mynativelib2/android_arm_armv7-a-neon_shared/mynativelib2.so -> arm/lib/mynativelib2.so
+`),
+ )
+}
+
+// Verify that when the shared library has some common and some arch specific properties that the generated
+// snapshot is optimized properly.
+func TestSnapshotWithCcSharedLibraryCommonProperties(t *testing.T) {
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ native_shared_libs: ["mynativelib"],
+ }
+
+ cc_library_shared {
+ name: "mynativelib",
+ srcs: [
+ "Test.cpp",
+ "aidl/foo/bar/Test.aidl",
+ ],
+ export_include_dirs: ["include"],
+ arch: {
+ arm64: {
+ export_system_include_dirs: ["arm64/include"],
+ },
+ },
+ stl: "none",
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+ name: "mysdk_mynativelib@current",
+ sdk_member_name: "mynativelib",
+ installable: false,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/mynativelib.so"],
+ export_system_include_dirs: ["arm64/include/arm64/include"],
+ },
+ arm: {
+ srcs: ["arm/lib/mynativelib.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "mynativelib",
+ prefer: false,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/mynativelib.so"],
+ export_system_include_dirs: ["arm64/include/arm64/include"],
+ },
+ arm: {
+ srcs: ["arm/lib/mynativelib.so"],
+ },
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ native_shared_libs: ["mysdk_mynativelib@current"],
+}
+`),
+ checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
+arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
+ )
+}
+
+func TestSnapshotWithCcBinary(t *testing.T) {
+ result := testSdkWithCc(t, `
+ module_exports {
+ name: "mymodule_exports",
+ native_binaries: ["mynativebinary"],
+ }
+
+ cc_binary {
+ name: "mynativebinary",
+ srcs: [
+ "Test.cpp",
+ ],
+ compile_multilib: "both",
+ stl: "none",
+ }
+ `)
+
+ result.CheckSnapshot("mymodule_exports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_binary {
+ name: "mymodule_exports_mynativebinary@current",
+ sdk_member_name: "mynativebinary",
+ installable: false,
+ compile_multilib: "both",
+ arch: {
+ arm64: {
+ srcs: ["arm64/bin/mynativebinary"],
+ },
+ arm: {
+ srcs: ["arm/bin/mynativebinary"],
+ },
+ },
+}
+
+cc_prebuilt_binary {
+ name: "mynativebinary",
+ prefer: false,
+ compile_multilib: "both",
+ arch: {
+ arm64: {
+ srcs: ["arm64/bin/mynativebinary"],
+ },
+ arm: {
+ srcs: ["arm/bin/mynativebinary"],
+ },
+ },
+}
+
+module_exports_snapshot {
+ name: "mymodule_exports@current",
+ native_binaries: ["mymodule_exports_mynativebinary@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/mynativebinary/android_arm64_armv8-a/mynativebinary -> arm64/bin/mynativebinary
+.intermediates/mynativebinary/android_arm_armv7-a-neon/mynativebinary -> arm/bin/mynativebinary
+`),
+ )
+}
+
+func TestMultipleHostOsTypesSnapshotWithCcBinary(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithCc(t, `
+ module_exports {
+ name: "myexports",
+ device_supported: false,
+ host_supported: true,
+ native_binaries: ["mynativebinary"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }
+
+ cc_binary {
+ name: "mynativebinary",
+ device_supported: false,
+ host_supported: true,
+ srcs: [
+ "Test.cpp",
+ ],
+ compile_multilib: "both",
+ stl: "none",
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }
+ `)
+
+ result.CheckSnapshot("myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_binary {
+ name: "myexports_mynativebinary@current",
+ sdk_member_name: "mynativebinary",
+ device_supported: false,
+ host_supported: true,
+ installable: false,
+ target: {
+ linux_glibc: {
+ compile_multilib: "both",
+ },
+ linux_glibc_x86_64: {
+ srcs: ["linux_glibc/x86_64/bin/mynativebinary"],
+ },
+ linux_glibc_x86: {
+ srcs: ["linux_glibc/x86/bin/mynativebinary"],
+ },
+ windows: {
+ compile_multilib: "64",
+ },
+ windows_x86_64: {
+ srcs: ["windows/x86_64/bin/mynativebinary.exe"],
+ },
+ },
+}
+
+cc_prebuilt_binary {
+ name: "mynativebinary",
+ prefer: false,
+ device_supported: false,
+ host_supported: true,
+ target: {
+ linux_glibc: {
+ compile_multilib: "both",
+ },
+ linux_glibc_x86_64: {
+ srcs: ["linux_glibc/x86_64/bin/mynativebinary"],
+ },
+ linux_glibc_x86: {
+ srcs: ["linux_glibc/x86/bin/mynativebinary"],
+ },
+ windows: {
+ compile_multilib: "64",
+ },
+ windows_x86_64: {
+ srcs: ["windows/x86_64/bin/mynativebinary.exe"],
+ },
+ },
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ device_supported: false,
+ host_supported: true,
+ native_binaries: ["myexports_mynativebinary@current"],
+ target: {
+ windows: {
+ compile_multilib: "64",
+ },
+ },
+}
+`),
+ checkAllCopyRules(`
+.intermediates/mynativebinary/linux_glibc_x86_64/mynativebinary -> linux_glibc/x86_64/bin/mynativebinary
+.intermediates/mynativebinary/linux_glibc_x86/mynativebinary -> linux_glibc/x86/bin/mynativebinary
+.intermediates/mynativebinary/windows_x86_64/mynativebinary.exe -> windows/x86_64/bin/mynativebinary.exe
+`),
+ )
+}
+
+func TestSnapshotWithCcSharedLibrary(t *testing.T) {
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ native_shared_libs: ["mynativelib"],
+ }
+
+ cc_library_shared {
+ name: "mynativelib",
+ srcs: [
+ "Test.cpp",
+ "aidl/foo/bar/Test.aidl",
+ ],
+ apex_available: ["apex1", "apex2"],
+ export_include_dirs: ["include"],
+ aidl: {
+ export_aidl_headers: true,
+ },
+ stl: "none",
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+ name: "mysdk_mynativelib@current",
+ sdk_member_name: "mynativelib",
+ apex_available: [
+ "apex1",
+ "apex2",
+ ],
+ installable: false,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/mynativelib.so"],
+ export_include_dirs: ["arm64/include_gen/mynativelib"],
+ },
+ arm: {
+ srcs: ["arm/lib/mynativelib.so"],
+ export_include_dirs: ["arm/include_gen/mynativelib"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "mynativelib",
+ prefer: false,
+ apex_available: [
+ "apex1",
+ "apex2",
+ ],
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/mynativelib.so"],
+ export_include_dirs: ["arm64/include_gen/mynativelib"],
+ },
+ arm: {
+ srcs: ["arm/lib/mynativelib.so"],
+ export_include_dirs: ["arm/include_gen/mynativelib"],
+ },
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ native_shared_libs: ["mysdk_mynativelib@current"],
+}
+`),
+ checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+`),
+ )
+}
+
+func TestSnapshotWithCcSharedLibrarySharedLibs(t *testing.T) {
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ native_shared_libs: [
+ "mynativelib",
+ "myothernativelib",
+ "mysystemnativelib",
+ ],
+ }
+
+ cc_library {
+ name: "mysystemnativelib",
+ srcs: [
+ "Test.cpp",
+ ],
+ stl: "none",
+ }
+
+ cc_library_shared {
+ name: "myothernativelib",
+ srcs: [
+ "Test.cpp",
+ ],
+ system_shared_libs: [
+ // A reference to a library that is not an sdk member. Uses libm as that
+ // is in the default set of modules available to this test and so is available
+ // both here and also when the generated Android.bp file is tested in
+ // CheckSnapshot(). This ensures that the system_shared_libs property correctly
+ // handles references to modules that are not sdk members.
+ "libm",
+ ],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "mynativelib",
+ srcs: [
+ "Test.cpp",
+ ],
+ shared_libs: [
+ // A reference to another sdk member.
+ "myothernativelib",
+ ],
+ target: {
+ android: {
+ shared: {
+ shared_libs: [
+ // A reference to a library that is not an sdk member. The libc library
+ // is used here to check that the shared_libs property is handled correctly
+ // in a similar way to how libm is used to check system_shared_libs above.
+ "libc",
+ ],
+ },
+ },
+ },
+ stl: "none",
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+ name: "mysdk_mynativelib@current",
+ sdk_member_name: "mynativelib",
+ installable: false,
+ stl: "none",
+ shared_libs: [
+ "mysdk_myothernativelib@current",
+ "libc",
+ ],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/mynativelib.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/mynativelib.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "mynativelib",
+ prefer: false,
+ stl: "none",
+ shared_libs: [
+ "myothernativelib",
+ "libc",
+ ],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/mynativelib.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/mynativelib.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "mysdk_myothernativelib@current",
+ sdk_member_name: "myothernativelib",
+ installable: false,
+ stl: "none",
+ system_shared_libs: ["libm"],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/myothernativelib.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/myothernativelib.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "myothernativelib",
+ prefer: false,
+ stl: "none",
+ system_shared_libs: ["libm"],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/myothernativelib.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/myothernativelib.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "mysdk_mysystemnativelib@current",
+ sdk_member_name: "mysystemnativelib",
+ installable: false,
+ stl: "none",
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/mysystemnativelib.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/mysystemnativelib.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "mysystemnativelib",
+ prefer: false,
+ stl: "none",
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/mysystemnativelib.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/mysystemnativelib.so"],
+ },
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ native_shared_libs: [
+ "mysdk_mynativelib@current",
+ "mysdk_myothernativelib@current",
+ "mysdk_mysystemnativelib@current",
+ ],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
+.intermediates/myothernativelib/android_arm64_armv8-a_shared/myothernativelib.so -> arm64/lib/myothernativelib.so
+.intermediates/myothernativelib/android_arm_armv7-a-neon_shared/myothernativelib.so -> arm/lib/myothernativelib.so
+.intermediates/mysystemnativelib/android_arm64_armv8-a_shared/mysystemnativelib.so -> arm64/lib/mysystemnativelib.so
+.intermediates/mysystemnativelib/android_arm_armv7-a-neon_shared/mysystemnativelib.so -> arm/lib/mysystemnativelib.so
+`),
+ )
+}
+
+func TestHostSnapshotWithCcSharedLibrary(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ device_supported: false,
+ host_supported: true,
+ native_shared_libs: ["mynativelib"],
+ }
+
+ cc_library_shared {
+ name: "mynativelib",
+ device_supported: false,
+ host_supported: true,
+ srcs: [
+ "Test.cpp",
+ "aidl/foo/bar/Test.aidl",
+ ],
+ export_include_dirs: ["include"],
+ aidl: {
+ export_aidl_headers: true,
+ },
+ stl: "none",
+ sdk_version: "minimum",
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+ name: "mysdk_mynativelib@current",
+ sdk_member_name: "mynativelib",
+ device_supported: false,
+ host_supported: true,
+ installable: false,
+ sdk_version: "minimum",
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ x86_64: {
+ srcs: ["x86_64/lib/mynativelib.so"],
+ export_include_dirs: ["x86_64/include_gen/mynativelib"],
+ },
+ x86: {
+ srcs: ["x86/lib/mynativelib.so"],
+ export_include_dirs: ["x86/include_gen/mynativelib"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "mynativelib",
+ prefer: false,
+ device_supported: false,
+ host_supported: true,
+ sdk_version: "minimum",
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ x86_64: {
+ srcs: ["x86_64/lib/mynativelib.so"],
+ export_include_dirs: ["x86_64/include_gen/mynativelib"],
+ },
+ x86: {
+ srcs: ["x86/lib/mynativelib.so"],
+ export_include_dirs: ["x86/include_gen/mynativelib"],
+ },
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ device_supported: false,
+ host_supported: true,
+ native_shared_libs: ["mysdk_mynativelib@current"],
+}
+`),
+ checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> x86_64/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> x86/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+`),
+ )
+}
+
+func TestMultipleHostOsTypesSnapshotWithCcSharedLibrary(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ device_supported: false,
+ host_supported: true,
+ native_shared_libs: ["mynativelib"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }
+
+ cc_library_shared {
+ name: "mynativelib",
+ device_supported: false,
+ host_supported: true,
+ srcs: [
+ "Test.cpp",
+ ],
+ stl: "none",
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+ name: "mysdk_mynativelib@current",
+ sdk_member_name: "mynativelib",
+ device_supported: false,
+ host_supported: true,
+ installable: false,
+ stl: "none",
+ target: {
+ linux_glibc_x86_64: {
+ srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
+ },
+ linux_glibc_x86: {
+ srcs: ["linux_glibc/x86/lib/mynativelib.so"],
+ },
+ windows_x86_64: {
+ srcs: ["windows/x86_64/lib/mynativelib.dll"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "mynativelib",
+ prefer: false,
+ device_supported: false,
+ host_supported: true,
+ stl: "none",
+ target: {
+ linux_glibc_x86_64: {
+ srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
+ },
+ linux_glibc_x86: {
+ srcs: ["linux_glibc/x86/lib/mynativelib.so"],
+ },
+ windows_x86_64: {
+ srcs: ["windows/x86_64/lib/mynativelib.dll"],
+ },
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ device_supported: false,
+ host_supported: true,
+ native_shared_libs: ["mysdk_mynativelib@current"],
+ target: {
+ windows: {
+ compile_multilib: "64",
+ },
+ },
+}
+`),
+ checkAllCopyRules(`
+.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> linux_glibc/x86_64/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> linux_glibc/x86/lib/mynativelib.so
+.intermediates/mynativelib/windows_x86_64_shared/mynativelib.dll -> windows/x86_64/lib/mynativelib.dll
+`),
+ )
+}
+
+func TestSnapshotWithCcStaticLibrary(t *testing.T) {
+ result := testSdkWithCc(t, `
+ module_exports {
+ name: "myexports",
+ native_static_libs: ["mynativelib"],
+ }
+
+ cc_library_static {
+ name: "mynativelib",
+ srcs: [
+ "Test.cpp",
+ "aidl/foo/bar/Test.aidl",
+ ],
+ export_include_dirs: ["include"],
+ aidl: {
+ export_aidl_headers: true,
+ },
+ stl: "none",
+ }
+ `)
+
+ result.CheckSnapshot("myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_static {
+ name: "myexports_mynativelib@current",
+ sdk_member_name: "mynativelib",
+ installable: false,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/mynativelib.a"],
+ export_include_dirs: ["arm64/include_gen/mynativelib"],
+ },
+ arm: {
+ srcs: ["arm/lib/mynativelib.a"],
+ export_include_dirs: ["arm/include_gen/mynativelib"],
+ },
+ },
+}
+
+cc_prebuilt_library_static {
+ name: "mynativelib",
+ prefer: false,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/mynativelib.a"],
+ export_include_dirs: ["arm64/include_gen/mynativelib"],
+ },
+ arm: {
+ srcs: ["arm/lib/mynativelib.a"],
+ export_include_dirs: ["arm/include_gen/mynativelib"],
+ },
+ },
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ native_static_libs: ["myexports_mynativelib@current"],
+}
+`),
+ checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a
+.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> arm/lib/mynativelib.a
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+`),
+ )
+}
+
+func TestHostSnapshotWithCcStaticLibrary(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithCc(t, `
+ module_exports {
+ name: "myexports",
+ device_supported: false,
+ host_supported: true,
+ native_static_libs: ["mynativelib"],
+ }
+
+ cc_library_static {
+ name: "mynativelib",
+ device_supported: false,
+ host_supported: true,
+ srcs: [
+ "Test.cpp",
+ "aidl/foo/bar/Test.aidl",
+ ],
+ export_include_dirs: ["include"],
+ aidl: {
+ export_aidl_headers: true,
+ },
+ stl: "none",
+ }
+ `)
+
+ result.CheckSnapshot("myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_static {
+ name: "myexports_mynativelib@current",
+ sdk_member_name: "mynativelib",
+ device_supported: false,
+ host_supported: true,
+ installable: false,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ x86_64: {
+ srcs: ["x86_64/lib/mynativelib.a"],
+ export_include_dirs: ["x86_64/include_gen/mynativelib"],
+ },
+ x86: {
+ srcs: ["x86/lib/mynativelib.a"],
+ export_include_dirs: ["x86/include_gen/mynativelib"],
+ },
+ },
+}
+
+cc_prebuilt_library_static {
+ name: "mynativelib",
+ prefer: false,
+ device_supported: false,
+ host_supported: true,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ x86_64: {
+ srcs: ["x86_64/lib/mynativelib.a"],
+ export_include_dirs: ["x86_64/include_gen/mynativelib"],
+ },
+ x86: {
+ srcs: ["x86/lib/mynativelib.a"],
+ export_include_dirs: ["x86/include_gen/mynativelib"],
+ },
+ },
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ device_supported: false,
+ host_supported: true,
+ native_static_libs: ["myexports_mynativelib@current"],
+}
+`),
+ checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> x86_64/lib/mynativelib.a
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/linux_glibc_x86_static/mynativelib.a -> x86/lib/mynativelib.a
+.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+`),
+ )
+}
+
+func TestSnapshotWithCcLibrary(t *testing.T) {
+ result := testSdkWithCc(t, `
+ module_exports {
+ name: "myexports",
+ native_libs: ["mynativelib"],
+ }
+
+ cc_library {
+ name: "mynativelib",
+ srcs: [
+ "Test.cpp",
+ ],
+ export_include_dirs: ["include"],
+ stl: "none",
+ }
+ `)
+
+ result.CheckSnapshot("myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library {
+ name: "myexports_mynativelib@current",
+ sdk_member_name: "mynativelib",
+ installable: false,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ arm64: {
+ static: {
+ srcs: ["arm64/lib/mynativelib.a"],
+ },
+ shared: {
+ srcs: ["arm64/lib/mynativelib.so"],
+ },
+ },
+ arm: {
+ static: {
+ srcs: ["arm/lib/mynativelib.a"],
+ },
+ shared: {
+ srcs: ["arm/lib/mynativelib.so"],
+ },
+ },
+ },
+}
+
+cc_prebuilt_library {
+ name: "mynativelib",
+ prefer: false,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ arm64: {
+ static: {
+ srcs: ["arm64/lib/mynativelib.a"],
+ },
+ shared: {
+ srcs: ["arm64/lib/mynativelib.so"],
+ },
+ },
+ arm: {
+ static: {
+ srcs: ["arm/lib/mynativelib.a"],
+ },
+ shared: {
+ srcs: ["arm/lib/mynativelib.so"],
+ },
+ },
+ },
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ native_libs: ["myexports_mynativelib@current"],
+}
+`),
+ checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> arm/lib/mynativelib.a
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
+ )
+}
+
+func TestHostSnapshotWithMultiLib64(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithCc(t, `
+ module_exports {
+ name: "myexports",
+ device_supported: false,
+ host_supported: true,
+ target: {
+ host: {
+ compile_multilib: "64",
+ },
+ },
+ native_static_libs: ["mynativelib"],
+ }
+
+ cc_library_static {
+ name: "mynativelib",
+ device_supported: false,
+ host_supported: true,
+ srcs: [
+ "Test.cpp",
+ "aidl/foo/bar/Test.aidl",
+ ],
+ export_include_dirs: ["include"],
+ aidl: {
+ export_aidl_headers: true,
+ },
+ stl: "none",
+ }
+ `)
+
+ result.CheckSnapshot("myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_static {
+ name: "myexports_mynativelib@current",
+ sdk_member_name: "mynativelib",
+ device_supported: false,
+ host_supported: true,
+ installable: false,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ x86_64: {
+ srcs: ["x86_64/lib/mynativelib.a"],
+ export_include_dirs: ["x86_64/include_gen/mynativelib"],
+ },
+ },
+}
+
+cc_prebuilt_library_static {
+ name: "mynativelib",
+ prefer: false,
+ device_supported: false,
+ host_supported: true,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+ arch: {
+ x86_64: {
+ srcs: ["x86_64/lib/mynativelib.a"],
+ export_include_dirs: ["x86_64/include_gen/mynativelib"],
+ },
+ },
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ device_supported: false,
+ host_supported: true,
+ native_static_libs: ["myexports_mynativelib@current"],
+ target: {
+ linux_glibc: {
+ compile_multilib: "64",
+ },
+ },
+}`),
+ checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> x86_64/lib/mynativelib.a
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+`),
+ )
+}
+
+func TestSnapshotWithCcHeadersLibrary(t *testing.T) {
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ native_header_libs: ["mynativeheaders"],
+ }
+
+ cc_library_headers {
+ name: "mynativeheaders",
+ export_include_dirs: ["include"],
+ stl: "none",
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_headers {
+ name: "mysdk_mynativeheaders@current",
+ sdk_member_name: "mynativeheaders",
+ stl: "none",
+ export_include_dirs: ["include/include"],
+}
+
+cc_prebuilt_library_headers {
+ name: "mynativeheaders",
+ prefer: false,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ native_header_libs: ["mysdk_mynativeheaders@current"],
+}
+`),
+ checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+`),
+ )
+}
+
+func TestHostSnapshotWithCcHeadersLibrary(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ device_supported: false,
+ host_supported: true,
+ native_header_libs: ["mynativeheaders"],
+ }
+
+ cc_library_headers {
+ name: "mynativeheaders",
+ device_supported: false,
+ host_supported: true,
+ export_include_dirs: ["include"],
+ stl: "none",
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_headers {
+ name: "mysdk_mynativeheaders@current",
+ sdk_member_name: "mynativeheaders",
+ device_supported: false,
+ host_supported: true,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+}
+
+cc_prebuilt_library_headers {
+ name: "mynativeheaders",
+ prefer: false,
+ device_supported: false,
+ host_supported: true,
+ stl: "none",
+ export_include_dirs: ["include/include"],
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ device_supported: false,
+ host_supported: true,
+ native_header_libs: ["mysdk_mynativeheaders@current"],
+}
+`),
+ checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+`),
+ )
+}
+
+func TestDeviceAndHostSnapshotWithCcHeadersLibrary(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ host_supported: true,
+ native_header_libs: ["mynativeheaders"],
+ }
+
+ cc_library_headers {
+ name: "mynativeheaders",
+ host_supported: true,
+ stl: "none",
+ export_system_include_dirs: ["include"],
+ target: {
+ android: {
+ export_include_dirs: ["include-android"],
+ },
+ host: {
+ export_include_dirs: ["include-host"],
+ },
+ },
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_headers {
+ name: "mysdk_mynativeheaders@current",
+ sdk_member_name: "mynativeheaders",
+ host_supported: true,
+ stl: "none",
+ export_system_include_dirs: ["include/include"],
+ target: {
+ android: {
+ export_include_dirs: ["include/include-android"],
+ },
+ linux_glibc: {
+ export_include_dirs: ["include/include-host"],
+ },
+ },
+}
+
+cc_prebuilt_library_headers {
+ name: "mynativeheaders",
+ prefer: false,
+ host_supported: true,
+ stl: "none",
+ export_system_include_dirs: ["include/include"],
+ target: {
+ android: {
+ export_include_dirs: ["include/include-android"],
+ },
+ linux_glibc: {
+ export_include_dirs: ["include/include-host"],
+ },
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ host_supported: true,
+ native_header_libs: ["mysdk_mynativeheaders@current"],
+}
+`),
+ checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+include-android/AndroidTest.h -> include/include-android/AndroidTest.h
+include-host/HostTest.h -> include/include-host/HostTest.h
+`),
+ )
+}
+
+func TestSystemSharedLibPropagation(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ native_shared_libs: ["sslnil", "sslempty", "sslnonempty"],
+ }
+
+ cc_library {
+ name: "sslnil",
+ host_supported: true,
+ }
+
+ cc_library {
+ name: "sslempty",
+ system_shared_libs: [],
+ }
+
+ cc_library {
+ name: "sslnonempty",
+ system_shared_libs: ["sslnil"],
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+ name: "mysdk_sslnil@current",
+ sdk_member_name: "sslnil",
+ installable: false,
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/sslnil.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/sslnil.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "sslnil",
+ prefer: false,
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/sslnil.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/sslnil.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "mysdk_sslempty@current",
+ sdk_member_name: "sslempty",
+ installable: false,
+ system_shared_libs: [],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/sslempty.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/sslempty.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "sslempty",
+ prefer: false,
+ system_shared_libs: [],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/sslempty.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/sslempty.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "mysdk_sslnonempty@current",
+ sdk_member_name: "sslnonempty",
+ installable: false,
+ system_shared_libs: ["mysdk_sslnil@current"],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/sslnonempty.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/sslnonempty.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "sslnonempty",
+ prefer: false,
+ system_shared_libs: ["sslnil"],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/sslnonempty.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/sslnonempty.so"],
+ },
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ native_shared_libs: [
+ "mysdk_sslnil@current",
+ "mysdk_sslempty@current",
+ "mysdk_sslnonempty@current",
+ ],
+}
+`))
+
+ result = testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ host_supported: true,
+ native_shared_libs: ["sslvariants"],
+ }
+
+ cc_library {
+ name: "sslvariants",
+ host_supported: true,
+ target: {
+ android: {
+ system_shared_libs: [],
+ },
+ },
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+ name: "mysdk_sslvariants@current",
+ sdk_member_name: "sslvariants",
+ host_supported: true,
+ installable: false,
+ target: {
+ android: {
+ system_shared_libs: [],
+ },
+ android_arm64: {
+ srcs: ["android/arm64/lib/sslvariants.so"],
+ },
+ android_arm: {
+ srcs: ["android/arm/lib/sslvariants.so"],
+ },
+ linux_glibc_x86_64: {
+ srcs: ["linux_glibc/x86_64/lib/sslvariants.so"],
+ },
+ linux_glibc_x86: {
+ srcs: ["linux_glibc/x86/lib/sslvariants.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "sslvariants",
+ prefer: false,
+ host_supported: true,
+ target: {
+ android: {
+ system_shared_libs: [],
+ },
+ android_arm64: {
+ srcs: ["android/arm64/lib/sslvariants.so"],
+ },
+ android_arm: {
+ srcs: ["android/arm/lib/sslvariants.so"],
+ },
+ linux_glibc_x86_64: {
+ srcs: ["linux_glibc/x86_64/lib/sslvariants.so"],
+ },
+ linux_glibc_x86: {
+ srcs: ["linux_glibc/x86/lib/sslvariants.so"],
+ },
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ host_supported: true,
+ native_shared_libs: ["mysdk_sslvariants@current"],
+}
+`))
+}
+
+func TestStubsLibrary(t *testing.T) {
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ native_shared_libs: ["stubslib"],
+ }
+
+ cc_library {
+ name: "internaldep",
+ }
+
+ cc_library {
+ name: "stubslib",
+ shared_libs: ["internaldep"],
+ stubs: {
+ symbol_file: "some/where/stubslib.map.txt",
+ versions: ["1", "2", "3"],
+ },
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+ name: "mysdk_stubslib@current",
+ sdk_member_name: "stubslib",
+ installable: false,
+ stubs: {
+ versions: ["3"],
+ },
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/stubslib.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/stubslib.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "stubslib",
+ prefer: false,
+ stubs: {
+ versions: ["3"],
+ },
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/stubslib.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/stubslib.so"],
+ },
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ native_shared_libs: ["mysdk_stubslib@current"],
+}
+`))
+}
+
+func TestDeviceAndHostSnapshotWithStubsLibrary(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ host_supported: true,
+ native_shared_libs: ["stubslib"],
+ }
+
+ cc_library {
+ name: "internaldep",
+ host_supported: true,
+ }
+
+ cc_library {
+ name: "stubslib",
+ host_supported: true,
+ shared_libs: ["internaldep"],
+ stubs: {
+ symbol_file: "some/where/stubslib.map.txt",
+ versions: ["1", "2", "3"],
+ },
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+ name: "mysdk_stubslib@current",
+ sdk_member_name: "stubslib",
+ host_supported: true,
+ installable: false,
+ stubs: {
+ versions: ["3"],
+ },
+ target: {
+ android_arm64: {
+ srcs: ["android/arm64/lib/stubslib.so"],
+ },
+ android_arm: {
+ srcs: ["android/arm/lib/stubslib.so"],
+ },
+ linux_glibc_x86_64: {
+ srcs: ["linux_glibc/x86_64/lib/stubslib.so"],
+ },
+ linux_glibc_x86: {
+ srcs: ["linux_glibc/x86/lib/stubslib.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "stubslib",
+ prefer: false,
+ host_supported: true,
+ stubs: {
+ versions: ["3"],
+ },
+ target: {
+ android_arm64: {
+ srcs: ["android/arm64/lib/stubslib.so"],
+ },
+ android_arm: {
+ srcs: ["android/arm/lib/stubslib.so"],
+ },
+ linux_glibc_x86_64: {
+ srcs: ["linux_glibc/x86_64/lib/stubslib.so"],
+ },
+ linux_glibc_x86: {
+ srcs: ["linux_glibc/x86/lib/stubslib.so"],
+ },
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ host_supported: true,
+ native_shared_libs: ["mysdk_stubslib@current"],
+}
+`))
+}
diff --git a/sdk/exports.go b/sdk/exports.go
new file mode 100644
index 000000000..d3130574e
--- /dev/null
+++ b/sdk/exports.go
@@ -0,0 +1,36 @@
+// Copyright (C) 2019 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.
+
+package sdk
+
+import "android/soong/android"
+
+func init() {
+ android.RegisterModuleType("module_exports", ModuleExportsFactory)
+ android.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory)
+}
+
+// module_exports defines the exports of a mainline module. The exports are Soong modules
+// which are required by Soong modules that are not part of the mainline module.
+func ModuleExportsFactory() android.Module {
+ return newSdkModule(true)
+}
+
+// module_exports_snapshot is a versioned snapshot of prebuilt versions of all the exports
+// of a mainline module.
+func ModuleExportsSnapshotsFactory() android.Module {
+ s := newSdkModule(true)
+ s.properties.Snapshot = true
+ return s
+}
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
new file mode 100644
index 000000000..20e25212c
--- /dev/null
+++ b/sdk/exports_test.go
@@ -0,0 +1,66 @@
+// Copyright 2019 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 sdk
+
+import (
+ "testing"
+)
+
+// Ensure that module_exports generates a module_exports_snapshot module.
+func TestModuleExportsSnapshot(t *testing.T) {
+ packageBp := `
+ module_exports {
+ name: "myexports",
+ java_libs: [
+ "myjavalib",
+ ],
+ }
+
+ java_library {
+ name: "myjavalib",
+ srcs: ["Test.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ }
+ `
+
+ result := testSdkWithFs(t, ``,
+ map[string][]byte{
+ "package/Test.java": nil,
+ "package/Android.bp": []byte(packageBp),
+ })
+
+ result.CheckSnapshot("myexports", "package",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "myexports_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ jars: ["java/myjavalib.jar"],
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ java_libs: ["myexports_myjavalib@current"],
+}
+`))
+}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
new file mode 100644
index 000000000..db395c582
--- /dev/null
+++ b/sdk/java_sdk_test.go
@@ -0,0 +1,1560 @@
+// Copyright (C) 2019 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.
+
+package sdk
+
+import (
+ "testing"
+)
+
+func testSdkWithJava(t *testing.T, bp string) *testSdkResult {
+ t.Helper()
+
+ fs := map[string][]byte{
+ "Test.java": nil,
+ "aidl/foo/bar/Test.aidl": nil,
+
+ // For java_sdk_library
+ "api/current.txt": nil,
+ "api/removed.txt": nil,
+ "api/system-current.txt": nil,
+ "api/system-removed.txt": nil,
+ "api/test-current.txt": nil,
+ "api/test-removed.txt": nil,
+ "api/module-lib-current.txt": nil,
+ "api/module-lib-removed.txt": nil,
+ "api/system-server-current.txt": nil,
+ "api/system-server-removed.txt": nil,
+ "build/soong/scripts/gen-java-current-api-files.sh": nil,
+ }
+
+ // for java_sdk_library tests
+ bp = `
+java_system_modules_import {
+ name: "core-current-stubs-system-modules",
+}
+java_system_modules_import {
+ name: "core-platform-api-stubs-system-modules",
+}
+java_import {
+ name: "core.platform.api.stubs",
+}
+java_import {
+ name: "android_stubs_current",
+}
+java_import {
+ name: "android_system_stubs_current",
+}
+java_import {
+ name: "android_test_stubs_current",
+}
+java_import {
+ name: "android_module_lib_stubs_current",
+}
+java_import {
+ name: "android_system_server_stubs_current",
+}
+java_import {
+ name: "core-lambda-stubs",
+ sdk_version: "none",
+}
+java_import {
+ name: "ext",
+ sdk_version: "none",
+}
+java_import {
+ name: "framework",
+ sdk_version: "none",
+}
+` + bp
+
+ return testSdkWithFs(t, bp, fs)
+}
+
+// Contains tests for SDK members provided by the java package.
+
+func TestBasicSdkWithJavaLibrary(t *testing.T) {
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ java_header_libs: ["sdkmember"],
+ }
+
+ sdk_snapshot {
+ name: "mysdk@1",
+ java_header_libs: ["sdkmember_mysdk_1"],
+ }
+
+ sdk_snapshot {
+ name: "mysdk@2",
+ java_header_libs: ["sdkmember_mysdk_2"],
+ }
+
+ java_library {
+ name: "sdkmember",
+ srcs: ["Test.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ host_supported: true,
+ }
+
+ java_import {
+ name: "sdkmember_mysdk_1",
+ sdk_member_name: "sdkmember",
+ host_supported: true,
+ }
+
+ java_import {
+ name: "sdkmember_mysdk_2",
+ sdk_member_name: "sdkmember",
+ host_supported: true,
+ }
+
+ java_library {
+ name: "myjavalib",
+ srcs: ["Test.java"],
+ libs: ["sdkmember"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ host_supported: true,
+ apex_available: [
+ "myapex",
+ "myapex2",
+ ],
+ }
+
+ apex {
+ name: "myapex",
+ java_libs: ["myjavalib"],
+ uses_sdks: ["mysdk@1"],
+ key: "myapex.key",
+ certificate: ":myapex.cert",
+ }
+
+ apex {
+ name: "myapex2",
+ java_libs: ["myjavalib"],
+ uses_sdks: ["mysdk@2"],
+ key: "myapex.key",
+ certificate: ":myapex.cert",
+ }
+ `)
+
+ sdkMemberV1 := result.ctx.ModuleForTests("sdkmember_mysdk_1", "android_common_myapex").Rule("combineJar").Output
+ sdkMemberV2 := result.ctx.ModuleForTests("sdkmember_mysdk_2", "android_common_myapex2").Rule("combineJar").Output
+
+ javalibForMyApex := result.ctx.ModuleForTests("myjavalib", "android_common_myapex")
+ javalibForMyApex2 := result.ctx.ModuleForTests("myjavalib", "android_common_myapex2")
+
+ // Depending on the uses_sdks value, different libs are linked
+ ensureListContains(t, pathsToStrings(javalibForMyApex.Rule("javac").Implicits), sdkMemberV1.String())
+ ensureListContains(t, pathsToStrings(javalibForMyApex2.Rule("javac").Implicits), sdkMemberV2.String())
+}
+
+func TestSnapshotWithJavaHeaderLibrary(t *testing.T) {
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ java_header_libs: ["myjavalib"],
+ }
+
+ java_library {
+ name: "myjavalib",
+ srcs: ["Test.java"],
+ aidl: {
+ export_include_dirs: ["aidl"],
+ },
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ host_supported: true,
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ jars: ["java/myjavalib.jar"],
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ java_header_libs: ["mysdk_myjavalib@current"],
+}
+
+`),
+ checkAllCopyRules(`
+.intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/myjavalib.jar
+aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
+`),
+ )
+}
+
+func TestHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ device_supported: false,
+ host_supported: true,
+ java_header_libs: ["myjavalib"],
+ }
+
+ java_library {
+ name: "myjavalib",
+ device_supported: false,
+ host_supported: true,
+ srcs: ["Test.java"],
+ aidl: {
+ export_include_dirs: ["aidl"],
+ },
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ device_supported: false,
+ host_supported: true,
+ jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ device_supported: false,
+ host_supported: true,
+ jars: ["java/myjavalib.jar"],
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ device_supported: false,
+ host_supported: true,
+ java_header_libs: ["mysdk_myjavalib@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/myjavalib.jar
+aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
+`),
+ )
+}
+
+func TestDeviceAndHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ host_supported: true,
+ java_header_libs: ["myjavalib"],
+ }
+
+ java_library {
+ name: "myjavalib",
+ host_supported: true,
+ srcs: ["Test.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ host_supported: true,
+ target: {
+ android: {
+ jars: ["java/android/myjavalib.jar"],
+ },
+ linux_glibc: {
+ jars: ["java/linux_glibc/myjavalib.jar"],
+ },
+ },
+}
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ host_supported: true,
+ target: {
+ android: {
+ jars: ["java/android/myjavalib.jar"],
+ },
+ linux_glibc: {
+ jars: ["java/linux_glibc/myjavalib.jar"],
+ },
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ host_supported: true,
+ java_header_libs: ["mysdk_myjavalib@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/android/myjavalib.jar
+.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/linux_glibc/myjavalib.jar
+`),
+ )
+}
+
+func TestSnapshotWithJavaImplLibrary(t *testing.T) {
+ result := testSdkWithJava(t, `
+ module_exports {
+ name: "myexports",
+ java_libs: ["myjavalib"],
+ }
+
+ java_library {
+ name: "myjavalib",
+ srcs: ["Test.java"],
+ aidl: {
+ export_include_dirs: ["aidl"],
+ },
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ host_supported: true,
+ }
+ `)
+
+ result.CheckSnapshot("myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "myexports_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ jars: ["java/myjavalib.jar"],
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ java_libs: ["myexports_myjavalib@current"],
+}
+
+`),
+ checkAllCopyRules(`
+.intermediates/myjavalib/android_common/javac/myjavalib.jar -> java/myjavalib.jar
+aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
+`),
+ )
+}
+
+func TestHostSnapshotWithJavaImplLibrary(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithJava(t, `
+ module_exports {
+ name: "myexports",
+ device_supported: false,
+ host_supported: true,
+ java_libs: ["myjavalib"],
+ }
+
+ java_library {
+ name: "myjavalib",
+ device_supported: false,
+ host_supported: true,
+ srcs: ["Test.java"],
+ aidl: {
+ export_include_dirs: ["aidl"],
+ },
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ }
+ `)
+
+ result.CheckSnapshot("myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "myexports_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ device_supported: false,
+ host_supported: true,
+ jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ device_supported: false,
+ host_supported: true,
+ jars: ["java/myjavalib.jar"],
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ device_supported: false,
+ host_supported: true,
+ java_libs: ["myexports_myjavalib@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/myjavalib.jar
+aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
+`),
+ )
+}
+
+func TestSnapshotWithJavaTest(t *testing.T) {
+ result := testSdkWithJava(t, `
+ module_exports {
+ name: "myexports",
+ java_tests: ["myjavatests"],
+ }
+
+ java_test {
+ name: "myjavatests",
+ srcs: ["Test.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ host_supported: true,
+ }
+ `)
+
+ result.CheckSnapshot("myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_test_import {
+ name: "myexports_myjavatests@current",
+ sdk_member_name: "myjavatests",
+ jars: ["java/myjavatests.jar"],
+ test_config: "java/myjavatests-AndroidTest.xml",
+}
+
+java_test_import {
+ name: "myjavatests",
+ prefer: false,
+ jars: ["java/myjavatests.jar"],
+ test_config: "java/myjavatests-AndroidTest.xml",
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ java_tests: ["myexports_myjavatests@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myjavatests/android_common/javac/myjavatests.jar -> java/myjavatests.jar
+.intermediates/myjavatests/android_common/myjavatests.config -> java/myjavatests-AndroidTest.xml
+`),
+ )
+}
+
+func TestHostSnapshotWithJavaTest(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithJava(t, `
+ module_exports {
+ name: "myexports",
+ device_supported: false,
+ host_supported: true,
+ java_tests: ["myjavatests"],
+ }
+
+ java_test {
+ name: "myjavatests",
+ device_supported: false,
+ host_supported: true,
+ srcs: ["Test.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ }
+ `)
+
+ result.CheckSnapshot("myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_test_import {
+ name: "myexports_myjavatests@current",
+ sdk_member_name: "myjavatests",
+ device_supported: false,
+ host_supported: true,
+ jars: ["java/myjavatests.jar"],
+ test_config: "java/myjavatests-AndroidTest.xml",
+}
+
+java_test_import {
+ name: "myjavatests",
+ prefer: false,
+ device_supported: false,
+ host_supported: true,
+ jars: ["java/myjavatests.jar"],
+ test_config: "java/myjavatests-AndroidTest.xml",
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ device_supported: false,
+ host_supported: true,
+ java_tests: ["myexports_myjavatests@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myjavatests/linux_glibc_common/javac/myjavatests.jar -> java/myjavatests.jar
+.intermediates/myjavatests/linux_glibc_common/myjavatests.config -> java/myjavatests-AndroidTest.xml
+`),
+ )
+}
+
+func testSdkWithDroidstubs(t *testing.T, bp string) *testSdkResult {
+ t.Helper()
+
+ fs := map[string][]byte{
+ "foo/bar/Foo.java": nil,
+ "stubs-sources/foo/bar/Foo.java": nil,
+ }
+ return testSdkWithFs(t, bp, fs)
+}
+
+// Note: This test does not verify that a droidstubs can be referenced, either
+// directly or indirectly from an APEX as droidstubs can never be a part of an
+// apex.
+func TestBasicSdkWithDroidstubs(t *testing.T) {
+ testSdkWithDroidstubs(t, `
+ sdk {
+ name: "mysdk",
+ stubs_sources: ["mystub"],
+ }
+ sdk_snapshot {
+ name: "mysdk@10",
+ stubs_sources: ["mystub_mysdk@10"],
+ }
+ prebuilt_stubs_sources {
+ name: "mystub_mysdk@10",
+ sdk_member_name: "mystub",
+ srcs: ["stubs-sources/foo/bar/Foo.java"],
+ }
+ droidstubs {
+ name: "mystub",
+ srcs: ["foo/bar/Foo.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ }
+ java_library {
+ name: "myjavalib",
+ srcs: [":mystub"],
+ sdk_version: "none",
+ system_modules: "none",
+ }
+ `)
+}
+
+func TestSnapshotWithDroidstubs(t *testing.T) {
+ result := testSdkWithDroidstubs(t, `
+ module_exports {
+ name: "myexports",
+ stubs_sources: ["myjavaapistubs"],
+ }
+
+ droidstubs {
+ name: "myjavaapistubs",
+ srcs: ["foo/bar/Foo.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ }
+ `)
+
+ result.CheckSnapshot("myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_stubs_sources {
+ name: "myexports_myjavaapistubs@current",
+ sdk_member_name: "myjavaapistubs",
+ srcs: ["java/myjavaapistubs_stubs_sources"],
+}
+
+prebuilt_stubs_sources {
+ name: "myjavaapistubs",
+ prefer: false,
+ srcs: ["java/myjavaapistubs_stubs_sources"],
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ stubs_sources: ["myexports_myjavaapistubs@current"],
+}
+
+`),
+ checkAllCopyRules(""),
+ checkMergeZips(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"),
+ )
+}
+
+func TestHostSnapshotWithDroidstubs(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithDroidstubs(t, `
+ module_exports {
+ name: "myexports",
+ device_supported: false,
+ host_supported: true,
+ stubs_sources: ["myjavaapistubs"],
+ }
+
+ droidstubs {
+ name: "myjavaapistubs",
+ device_supported: false,
+ host_supported: true,
+ srcs: ["foo/bar/Foo.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ }
+ `)
+
+ result.CheckSnapshot("myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_stubs_sources {
+ name: "myexports_myjavaapistubs@current",
+ sdk_member_name: "myjavaapistubs",
+ device_supported: false,
+ host_supported: true,
+ srcs: ["java/myjavaapistubs_stubs_sources"],
+}
+
+prebuilt_stubs_sources {
+ name: "myjavaapistubs",
+ prefer: false,
+ device_supported: false,
+ host_supported: true,
+ srcs: ["java/myjavaapistubs_stubs_sources"],
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ device_supported: false,
+ host_supported: true,
+ stubs_sources: ["myexports_myjavaapistubs@current"],
+}
+`),
+ checkAllCopyRules(""),
+ checkMergeZips(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"),
+ )
+}
+
+func TestSnapshotWithJavaSystemModules(t *testing.T) {
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ java_header_libs: ["exported-system-module"],
+ java_system_modules: ["my-system-modules"],
+ }
+
+ java_system_modules {
+ name: "my-system-modules",
+ libs: ["system-module", "exported-system-module"],
+ }
+
+ java_library {
+ name: "system-module",
+ srcs: ["Test.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ }
+
+ java_library {
+ name: "exported-system-module",
+ srcs: ["Test.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "mysdk_exported-system-module@current",
+ sdk_member_name: "exported-system-module",
+ jars: ["java/exported-system-module.jar"],
+}
+
+java_import {
+ name: "exported-system-module",
+ prefer: false,
+ jars: ["java/exported-system-module.jar"],
+}
+
+java_import {
+ name: "mysdk_system-module@current",
+ sdk_member_name: "system-module",
+ visibility: ["//visibility:private"],
+ jars: ["java/system-module.jar"],
+}
+
+java_import {
+ name: "mysdk_system-module",
+ prefer: false,
+ visibility: ["//visibility:private"],
+ jars: ["java/system-module.jar"],
+}
+
+java_system_modules_import {
+ name: "mysdk_my-system-modules@current",
+ sdk_member_name: "my-system-modules",
+ libs: [
+ "mysdk_system-module@current",
+ "mysdk_exported-system-module@current",
+ ],
+}
+
+java_system_modules_import {
+ name: "my-system-modules",
+ prefer: false,
+ libs: [
+ "mysdk_system-module",
+ "exported-system-module",
+ ],
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ java_header_libs: ["mysdk_exported-system-module@current"],
+ java_system_modules: ["mysdk_my-system-modules@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/exported-system-module/android_common/turbine-combined/exported-system-module.jar -> java/exported-system-module.jar
+.intermediates/system-module/android_common/turbine-combined/system-module.jar -> java/system-module.jar
+`),
+ )
+}
+
+func TestHostSnapshotWithJavaSystemModules(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ device_supported: false,
+ host_supported: true,
+ java_system_modules: ["my-system-modules"],
+ }
+
+ java_system_modules {
+ name: "my-system-modules",
+ device_supported: false,
+ host_supported: true,
+ libs: ["system-module"],
+ }
+
+ java_library {
+ name: "system-module",
+ device_supported: false,
+ host_supported: true,
+ srcs: ["Test.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "mysdk_system-module@current",
+ sdk_member_name: "system-module",
+ visibility: ["//visibility:private"],
+ device_supported: false,
+ host_supported: true,
+ jars: ["java/system-module.jar"],
+}
+
+java_import {
+ name: "mysdk_system-module",
+ prefer: false,
+ visibility: ["//visibility:private"],
+ device_supported: false,
+ host_supported: true,
+ jars: ["java/system-module.jar"],
+}
+
+java_system_modules_import {
+ name: "mysdk_my-system-modules@current",
+ sdk_member_name: "my-system-modules",
+ device_supported: false,
+ host_supported: true,
+ libs: ["mysdk_system-module@current"],
+}
+
+java_system_modules_import {
+ name: "my-system-modules",
+ prefer: false,
+ device_supported: false,
+ host_supported: true,
+ libs: ["mysdk_system-module"],
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ device_supported: false,
+ host_supported: true,
+ java_system_modules: ["mysdk_my-system-modules@current"],
+}
+`),
+ checkAllCopyRules(".intermediates/system-module/linux_glibc_common/javac/system-module.jar -> java/system-module.jar"),
+ )
+}
+
+func TestDeviceAndHostSnapshotWithOsSpecificMembers(t *testing.T) {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ SkipIfNotLinux(t)
+
+ result := testSdkWithJava(t, `
+ module_exports {
+ name: "myexports",
+ host_supported: true,
+ java_libs: ["myjavalib"],
+ target: {
+ android: {
+ java_header_libs: ["androidjavalib"],
+ },
+ host: {
+ java_header_libs: ["hostjavalib"],
+ },
+ },
+ }
+
+ java_library {
+ name: "myjavalib",
+ host_supported: true,
+ srcs: ["Test.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ }
+
+ java_library {
+ name: "androidjavalib",
+ srcs: ["Test.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ }
+
+ java_library_host {
+ name: "hostjavalib",
+ srcs: ["Test.java"],
+ }
+ `)
+
+ result.CheckSnapshot("myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "myexports_hostjavalib@current",
+ sdk_member_name: "hostjavalib",
+ device_supported: false,
+ host_supported: true,
+ jars: ["java/hostjavalib.jar"],
+}
+
+java_import {
+ name: "hostjavalib",
+ prefer: false,
+ device_supported: false,
+ host_supported: true,
+ jars: ["java/hostjavalib.jar"],
+}
+
+java_import {
+ name: "myexports_androidjavalib@current",
+ sdk_member_name: "androidjavalib",
+ jars: ["java/androidjavalib.jar"],
+}
+
+java_import {
+ name: "androidjavalib",
+ prefer: false,
+ jars: ["java/androidjavalib.jar"],
+}
+
+java_import {
+ name: "myexports_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ host_supported: true,
+ target: {
+ android: {
+ jars: ["java/android/myjavalib.jar"],
+ },
+ linux_glibc: {
+ jars: ["java/linux_glibc/myjavalib.jar"],
+ },
+ },
+}
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ host_supported: true,
+ target: {
+ android: {
+ jars: ["java/android/myjavalib.jar"],
+ },
+ linux_glibc: {
+ jars: ["java/linux_glibc/myjavalib.jar"],
+ },
+ },
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ host_supported: true,
+ java_libs: ["myexports_myjavalib@current"],
+ target: {
+ android: {
+ java_header_libs: ["myexports_androidjavalib@current"],
+ },
+ linux_glibc: {
+ java_header_libs: ["myexports_hostjavalib@current"],
+ },
+ },
+}
+`),
+ checkAllCopyRules(`
+.intermediates/hostjavalib/linux_glibc_common/javac/hostjavalib.jar -> java/hostjavalib.jar
+.intermediates/androidjavalib/android_common/turbine-combined/androidjavalib.jar -> java/androidjavalib.jar
+.intermediates/myjavalib/android_common/javac/myjavalib.jar -> java/android/myjavalib.jar
+.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/linux_glibc/myjavalib.jar
+`),
+ )
+}
+
+func TestSnapshotWithJavaSdkLibrary(t *testing.T) {
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ java_sdk_libs: ["myjavalib"],
+ }
+
+ java_sdk_library {
+ name: "myjavalib",
+ apex_available: ["//apex_available:anyapex"],
+ srcs: ["Test.java"],
+ sdk_version: "current",
+ shared_library: false,
+ stubs_library_visibility: ["//other"],
+ stubs_source_visibility: ["//another"],
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ apex_available: ["//apex_available:anyapex"],
+ shared_library: false,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+ system: {
+ jars: ["sdk_library/system/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
+ current_api: "sdk_library/system/myjavalib.txt",
+ removed_api: "sdk_library/system/myjavalib-removed.txt",
+ sdk_version: "system_current",
+ },
+ test: {
+ jars: ["sdk_library/test/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/test/myjavalib_stub_sources"],
+ current_api: "sdk_library/test/myjavalib.txt",
+ removed_api: "sdk_library/test/myjavalib-removed.txt",
+ sdk_version: "test_current",
+ },
+}
+
+java_sdk_library_import {
+ name: "myjavalib",
+ prefer: false,
+ apex_available: ["//apex_available:anyapex"],
+ shared_library: false,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+ system: {
+ jars: ["sdk_library/system/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
+ current_api: "sdk_library/system/myjavalib.txt",
+ removed_api: "sdk_library/system/myjavalib-removed.txt",
+ sdk_version: "system_current",
+ },
+ test: {
+ jars: ["sdk_library/test/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/test/myjavalib_stub_sources"],
+ current_api: "sdk_library/test/myjavalib.txt",
+ removed_api: "sdk_library/test/myjavalib-removed.txt",
+ sdk_version: "test_current",
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ java_sdk_libs: ["mysdk_myjavalib@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.test/android_common/javac/myjavalib.stubs.test.jar -> sdk_library/test/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.test/android_common/myjavalib.stubs.source.test_api.txt -> sdk_library/test/myjavalib.txt
+.intermediates/myjavalib.stubs.source.test/android_common/myjavalib.stubs.source.test_removed.txt -> sdk_library/test/myjavalib-removed.txt
+`),
+ checkMergeZips(
+ ".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
+ ".intermediates/mysdk/common_os/tmp/sdk_library/system/myjavalib_stub_sources.zip",
+ ".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip"),
+ )
+}
+
+func TestSnapshotWithJavaSdkLibrary_SdkVersion_None(t *testing.T) {
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ java_sdk_libs: ["myjavalib"],
+ }
+
+ java_sdk_library {
+ name: "myjavalib",
+ srcs: ["Test.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ shared_library: true,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "none",
+ },
+}
+
+java_sdk_library_import {
+ name: "myjavalib",
+ prefer: false,
+ shared_library: true,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "none",
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ java_sdk_libs: ["mysdk_myjavalib@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+`),
+ checkMergeZips(
+ ".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
+ ),
+ )
+}
+
+func TestSnapshotWithJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ java_sdk_libs: ["myjavalib"],
+ }
+
+ java_sdk_library {
+ name: "myjavalib",
+ srcs: ["Test.java"],
+ sdk_version: "module_current",
+ public: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ shared_library: true,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "module_current",
+ },
+}
+
+java_sdk_library_import {
+ name: "myjavalib",
+ prefer: false,
+ shared_library: true,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "module_current",
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ java_sdk_libs: ["mysdk_myjavalib@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+`),
+ checkMergeZips(
+ ".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
+ ),
+ )
+}
+
+func TestSnapshotWithJavaSdkLibrary_ApiScopes(t *testing.T) {
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ java_sdk_libs: ["myjavalib"],
+ }
+
+ java_sdk_library {
+ name: "myjavalib",
+ apex_available: ["//apex_available:anyapex"],
+ srcs: ["Test.java"],
+ sdk_version: "current",
+ public: {
+ enabled: true,
+ },
+ system: {
+ enabled: true,
+ },
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ apex_available: ["//apex_available:anyapex"],
+ shared_library: true,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+ system: {
+ jars: ["sdk_library/system/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
+ current_api: "sdk_library/system/myjavalib.txt",
+ removed_api: "sdk_library/system/myjavalib-removed.txt",
+ sdk_version: "system_current",
+ },
+}
+
+java_sdk_library_import {
+ name: "myjavalib",
+ prefer: false,
+ apex_available: ["//apex_available:anyapex"],
+ shared_library: true,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+ system: {
+ jars: ["sdk_library/system/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
+ current_api: "sdk_library/system/myjavalib.txt",
+ removed_api: "sdk_library/system/myjavalib-removed.txt",
+ sdk_version: "system_current",
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ java_sdk_libs: ["mysdk_myjavalib@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
+`),
+ checkMergeZips(
+ ".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
+ ".intermediates/mysdk/common_os/tmp/sdk_library/system/myjavalib_stub_sources.zip",
+ ),
+ )
+}
+
+func TestSnapshotWithJavaSdkLibrary_ModuleLib(t *testing.T) {
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ java_sdk_libs: ["myjavalib"],
+ }
+
+ java_sdk_library {
+ name: "myjavalib",
+ apex_available: ["//apex_available:anyapex"],
+ srcs: ["Test.java"],
+ sdk_version: "current",
+ public: {
+ enabled: true,
+ },
+ system: {
+ enabled: true,
+ },
+ module_lib: {
+ enabled: true,
+ },
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ apex_available: ["//apex_available:anyapex"],
+ shared_library: true,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+ system: {
+ jars: ["sdk_library/system/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
+ current_api: "sdk_library/system/myjavalib.txt",
+ removed_api: "sdk_library/system/myjavalib-removed.txt",
+ sdk_version: "system_current",
+ },
+ module_lib: {
+ jars: ["sdk_library/module-lib/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/module-lib/myjavalib_stub_sources"],
+ current_api: "sdk_library/module-lib/myjavalib.txt",
+ removed_api: "sdk_library/module-lib/myjavalib-removed.txt",
+ sdk_version: "module_current",
+ },
+}
+
+java_sdk_library_import {
+ name: "myjavalib",
+ prefer: false,
+ apex_available: ["//apex_available:anyapex"],
+ shared_library: true,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+ system: {
+ jars: ["sdk_library/system/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
+ current_api: "sdk_library/system/myjavalib.txt",
+ removed_api: "sdk_library/system/myjavalib-removed.txt",
+ sdk_version: "system_current",
+ },
+ module_lib: {
+ jars: ["sdk_library/module-lib/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/module-lib/myjavalib_stub_sources"],
+ current_api: "sdk_library/module-lib/myjavalib.txt",
+ removed_api: "sdk_library/module-lib/myjavalib-removed.txt",
+ sdk_version: "module_current",
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ java_sdk_libs: ["mysdk_myjavalib@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.module_lib/android_common/javac/myjavalib.stubs.module_lib.jar -> sdk_library/module-lib/myjavalib-stubs.jar
+.intermediates/myjavalib.api.module_lib/android_common/myjavalib.api.module_lib_api.txt -> sdk_library/module-lib/myjavalib.txt
+.intermediates/myjavalib.api.module_lib/android_common/myjavalib.api.module_lib_removed.txt -> sdk_library/module-lib/myjavalib-removed.txt
+`),
+ checkMergeZips(
+ ".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
+ ".intermediates/mysdk/common_os/tmp/sdk_library/system/myjavalib_stub_sources.zip",
+ ".intermediates/mysdk/common_os/tmp/sdk_library/module-lib/myjavalib_stub_sources.zip",
+ ),
+ )
+}
+
+func TestSnapshotWithJavaSdkLibrary_SystemServer(t *testing.T) {
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ java_sdk_libs: ["myjavalib"],
+ }
+
+ java_sdk_library {
+ name: "myjavalib",
+ apex_available: ["//apex_available:anyapex"],
+ srcs: ["Test.java"],
+ sdk_version: "current",
+ public: {
+ enabled: true,
+ },
+ system_server: {
+ enabled: true,
+ },
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ apex_available: ["//apex_available:anyapex"],
+ shared_library: true,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+ system_server: {
+ jars: ["sdk_library/system-server/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/system-server/myjavalib_stub_sources"],
+ current_api: "sdk_library/system-server/myjavalib.txt",
+ removed_api: "sdk_library/system-server/myjavalib-removed.txt",
+ sdk_version: "system_server_current",
+ },
+}
+
+java_sdk_library_import {
+ name: "myjavalib",
+ prefer: false,
+ apex_available: ["//apex_available:anyapex"],
+ shared_library: true,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+ system_server: {
+ jars: ["sdk_library/system-server/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/system-server/myjavalib_stub_sources"],
+ current_api: "sdk_library/system-server/myjavalib.txt",
+ removed_api: "sdk_library/system-server/myjavalib-removed.txt",
+ sdk_version: "system_server_current",
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ java_sdk_libs: ["mysdk_myjavalib@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.system_server/android_common/javac/myjavalib.stubs.system_server.jar -> sdk_library/system-server/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.system_server/android_common/myjavalib.stubs.source.system_server_api.txt -> sdk_library/system-server/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system_server/android_common/myjavalib.stubs.source.system_server_removed.txt -> sdk_library/system-server/myjavalib-removed.txt
+`),
+ checkMergeZips(
+ ".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
+ ".intermediates/mysdk/common_os/tmp/sdk_library/system-server/myjavalib_stub_sources.zip",
+ ),
+ )
+}
+
+func TestSnapshotWithJavaSdkLibrary_NamingScheme(t *testing.T) {
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ java_sdk_libs: ["myjavalib"],
+ }
+
+ java_sdk_library {
+ name: "myjavalib",
+ apex_available: ["//apex_available:anyapex"],
+ srcs: ["Test.java"],
+ sdk_version: "current",
+ naming_scheme: "framework-modules",
+ public: {
+ enabled: true,
+ },
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ apex_available: ["//apex_available:anyapex"],
+ naming_scheme: "framework-modules",
+ shared_library: true,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+}
+
+java_sdk_library_import {
+ name: "myjavalib",
+ prefer: false,
+ apex_available: ["//apex_available:anyapex"],
+ naming_scheme: "framework-modules",
+ shared_library: true,
+ public: {
+ jars: ["sdk_library/public/myjavalib-stubs.jar"],
+ stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+ current_api: "sdk_library/public/myjavalib.txt",
+ removed_api: "sdk_library/public/myjavalib-removed.txt",
+ sdk_version: "current",
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ java_sdk_libs: ["mysdk_myjavalib@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myjavalib-stubs-publicapi/android_common/javac/myjavalib-stubs-publicapi.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib-stubs-srcs-publicapi/android_common/myjavalib-stubs-srcs-publicapi_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib-stubs-srcs-publicapi/android_common/myjavalib-stubs-srcs-publicapi_removed.txt -> sdk_library/public/myjavalib-removed.txt
+`),
+ checkMergeZips(
+ ".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
+ ),
+ )
+}
diff --git a/sdk/sdk.go b/sdk/sdk.go
new file mode 100644
index 000000000..cb5a6053d
--- /dev/null
+++ b/sdk/sdk.go
@@ -0,0 +1,468 @@
+// Copyright (C) 2019 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.
+
+package sdk
+
+import (
+ "fmt"
+ "io"
+ "reflect"
+ "strconv"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ // This package doesn't depend on the apex package, but import it to make its mutators to be
+ // registered before mutators in this package. See RegisterPostDepsMutators for more details.
+ _ "android/soong/apex"
+)
+
+func init() {
+ pctx.Import("android/soong/android")
+ pctx.Import("android/soong/java/config")
+
+ android.RegisterModuleType("sdk", SdkModuleFactory)
+ android.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
+ android.PreDepsMutators(RegisterPreDepsMutators)
+ android.PostDepsMutators(RegisterPostDepsMutators)
+}
+
+type sdk struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+
+ // The dynamically generated information about the registered SdkMemberType
+ dynamicSdkMemberTypes *dynamicSdkMemberTypes
+
+ // The dynamically created instance of the properties struct containing the sdk member
+ // list properties, e.g. java_libs.
+ dynamicMemberTypeListProperties interface{}
+
+ // Information about the OsType specific member variants associated with this variant.
+ //
+ // Set by OsType specific variants in the collectMembers() method and used by the
+ // CommonOS variant when building the snapshot. That work is all done on separate
+ // calls to the sdk.GenerateAndroidBuildActions method which is guaranteed to be
+ // called for the OsType specific variants before the CommonOS variant (because
+ // the latter depends on the former).
+ memberRefs []sdkMemberRef
+
+ // The multilib variants that are used by this sdk variant.
+ multilibUsages multilibUsage
+
+ properties sdkProperties
+
+ snapshotFile android.OptionalPath
+
+ // The builder, preserved for testing.
+ builderForTests *snapshotBuilder
+}
+
+type sdkProperties struct {
+ Snapshot bool `blueprint:"mutated"`
+
+ // True if this is a module_exports (or module_exports_snapshot) module type.
+ Module_exports bool `blueprint:"mutated"`
+}
+
+// Contains information about the sdk properties that list sdk members, e.g.
+// Java_header_libs.
+type sdkMemberListProperty struct {
+ // getter for the list of member names
+ getter func(properties interface{}) []string
+
+ // the type of member referenced in the list
+ memberType android.SdkMemberType
+
+ // the dependency tag used for items in this list that can be used to determine the memberType
+ // for a resolved dependency.
+ dependencyTag android.SdkMemberTypeDependencyTag
+}
+
+func (p *sdkMemberListProperty) propertyName() string {
+ return p.memberType.SdkPropertyName()
+}
+
+// Cache of dynamically generated dynamicSdkMemberTypes objects. The key is the pointer
+// to a slice of SdkMemberType instances held in android.SdkMemberTypes.
+var dynamicSdkMemberTypesMap android.OncePer
+
+// A dynamically generated set of member list properties and associated structure type.
+type dynamicSdkMemberTypes struct {
+ // The dynamically generated structure type.
+ //
+ // Contains one []string exported field for each android.SdkMemberTypes. The name of the field
+ // is the exported form of the value returned by SdkMemberType.SdkPropertyName().
+ propertiesStructType reflect.Type
+
+ // Information about each of the member type specific list properties.
+ memberListProperties []*sdkMemberListProperty
+}
+
+func (d *dynamicSdkMemberTypes) createMemberListProperties() interface{} {
+ return reflect.New(d.propertiesStructType).Interface()
+}
+
+func getDynamicSdkMemberTypes(registry *android.SdkMemberTypesRegistry) *dynamicSdkMemberTypes {
+
+ // Get a key that uniquely identifies the registry contents.
+ key := registry.UniqueOnceKey()
+
+ // Get the registered types.
+ registeredTypes := registry.RegisteredTypes()
+
+ // Get the cached value, creating new instance if necessary.
+ return dynamicSdkMemberTypesMap.Once(key, func() interface{} {
+ return createDynamicSdkMemberTypes(registeredTypes)
+ }).(*dynamicSdkMemberTypes)
+}
+
+// Create the dynamicSdkMemberTypes from the list of registered member types.
+//
+// A struct is created which contains one exported field per member type corresponding to
+// the SdkMemberType.SdkPropertyName() value.
+//
+// A list of sdkMemberListProperty instances is created, one per member type that provides:
+// * a reference to the member type.
+// * a getter for the corresponding field in the properties struct.
+// * a dependency tag that identifies the member type of a resolved dependency.
+//
+func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
+
+ var listProperties []*sdkMemberListProperty
+ var fields []reflect.StructField
+
+ // Iterate over the member types creating StructField and sdkMemberListProperty objects.
+ for f, memberType := range sdkMemberTypes {
+ p := memberType.SdkPropertyName()
+
+ // Create a dynamic exported field for the member type's property.
+ fields = append(fields, reflect.StructField{
+ Name: proptools.FieldNameForProperty(p),
+ Type: reflect.TypeOf([]string{}),
+ Tag: `android:"arch_variant"`,
+ })
+
+ // Copy the field index for use in the getter func as using the loop variable directly will
+ // cause all funcs to use the last value.
+ fieldIndex := f
+
+ // Create an sdkMemberListProperty for the member type.
+ memberListProperty := &sdkMemberListProperty{
+ getter: func(properties interface{}) []string {
+ // The properties is expected to be of the following form (where
+ // <Module_types> is the name of an SdkMemberType.SdkPropertyName().
+ // properties *struct {<Module_types> []string, ....}
+ //
+ // Although it accesses the field by index the following reflection code is equivalent to:
+ // *properties.<Module_types>
+ //
+ list := reflect.ValueOf(properties).Elem().Field(fieldIndex).Interface().([]string)
+ return list
+ },
+
+ memberType: memberType,
+
+ dependencyTag: android.DependencyTagForSdkMemberType(memberType),
+ }
+
+ listProperties = append(listProperties, memberListProperty)
+ }
+
+ // Create a dynamic struct from the collated fields.
+ propertiesStructType := reflect.StructOf(fields)
+
+ return &dynamicSdkMemberTypes{
+ memberListProperties: listProperties,
+ propertiesStructType: propertiesStructType,
+ }
+}
+
+// sdk defines an SDK which is a logical group of modules (e.g. native libs, headers, java libs, etc.)
+// which Mainline modules like APEX can choose to build with.
+func SdkModuleFactory() android.Module {
+ return newSdkModule(false)
+}
+
+func newSdkModule(moduleExports bool) *sdk {
+ s := &sdk{}
+ s.properties.Module_exports = moduleExports
+ // Get the dynamic sdk member type data for the currently registered sdk member types.
+ var registry *android.SdkMemberTypesRegistry
+ if moduleExports {
+ registry = android.ModuleExportsMemberTypes
+ } else {
+ registry = android.SdkMemberTypes
+ }
+ s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(registry)
+ // Create an instance of the dynamically created struct that contains all the
+ // properties for the member type specific list properties.
+ s.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberListProperties()
+ s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties)
+ android.InitCommonOSAndroidMultiTargetsArchModule(s, android.HostAndDeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(s)
+ android.AddLoadHook(s, func(ctx android.LoadHookContext) {
+ type props struct {
+ Compile_multilib *string
+ }
+ p := &props{Compile_multilib: proptools.StringPtr("both")}
+ ctx.AppendProperties(p)
+ })
+ return s
+}
+
+// sdk_snapshot is a versioned snapshot of an SDK. This is an auto-generated module.
+func SnapshotModuleFactory() android.Module {
+ s := newSdkModule(false)
+ s.properties.Snapshot = true
+ return s
+}
+
+func (s *sdk) memberListProperties() []*sdkMemberListProperty {
+ return s.dynamicSdkMemberTypes.memberListProperties
+}
+
+func (s *sdk) getExportedMembers() map[string]struct{} {
+ // Collect all the exported members.
+ exportedMembers := make(map[string]struct{})
+
+ for _, memberListProperty := range s.memberListProperties() {
+ names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
+
+ // Every member specified explicitly in the properties is exported by the sdk.
+ for _, name := range names {
+ exportedMembers[name] = struct{}{}
+ }
+ }
+
+ return exportedMembers
+}
+
+func (s *sdk) snapshot() bool {
+ return s.properties.Snapshot
+}
+
+func (s *sdk) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if s.snapshot() {
+ // We don't need to create a snapshot out of sdk_snapshot.
+ // That doesn't make sense. We need a snapshot to create sdk_snapshot.
+ return
+ }
+
+ // This method is guaranteed to be called on OsType specific variants before it is called
+ // on their corresponding CommonOS variant.
+ if !s.IsCommonOSVariant() {
+ // Update the OsType specific sdk variant with information about its members.
+ s.collectMembers(ctx)
+ } else {
+ // Get the OsType specific variants on which the CommonOS depends.
+ osSpecificVariants := android.GetOsSpecificVariantsOfCommonOSVariant(ctx)
+ var sdkVariants []*sdk
+ for _, m := range osSpecificVariants {
+ if sdkVariant, ok := m.(*sdk); ok {
+ sdkVariants = append(sdkVariants, sdkVariant)
+ }
+ }
+
+ // Generate the snapshot from the member info.
+ p := s.buildSnapshot(ctx, sdkVariants)
+ s.snapshotFile = android.OptionalPathForPath(p)
+ ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), s.Name()+"-current.zip", p)
+ }
+}
+
+func (s *sdk) AndroidMkEntries() []android.AndroidMkEntries {
+ if !s.snapshotFile.Valid() {
+ return []android.AndroidMkEntries{}
+ }
+
+ return []android.AndroidMkEntries{android.AndroidMkEntries{
+ Class: "FAKE",
+ OutputFile: s.snapshotFile,
+ DistFile: s.snapshotFile,
+ Include: "$(BUILD_PHONY_PACKAGE)",
+ ExtraFooters: []android.AndroidMkExtraFootersFunc{
+ func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ // Allow the sdk to be built by simply passing its name on the command line.
+ fmt.Fprintln(w, ".PHONY:", s.Name())
+ fmt.Fprintln(w, s.Name()+":", s.snapshotFile.String())
+ },
+ },
+ }}
+}
+
+// RegisterPreDepsMutators registers pre-deps mutators to support modules implementing SdkAware
+// interface and the sdk module type. This function has been made public to be called by tests
+// outside of the sdk package
+func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("SdkMember", memberMutator).Parallel()
+ ctx.TopDown("SdkMember_deps", memberDepsMutator).Parallel()
+ ctx.BottomUp("SdkMemberInterVersion", memberInterVersionMutator).Parallel()
+}
+
+// RegisterPostDepsMutators registers post-deps mutators to support modules implementing SdkAware
+// interface and the sdk module type. This function has been made public to be called by tests
+// outside of the sdk package
+func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
+ // These must run AFTER apexMutator. Note that the apex package is imported even though there is
+ // no direct dependency to the package here. sdkDepsMutator sets the SDK requirements from an
+ // APEX to its dependents. Since different versions of the same SDK can be used by different
+ // APEXes, the apex and its dependents (which includes the dependencies to the sdk members)
+ // should have been mutated for the apex before the SDK requirements are set.
+ ctx.TopDown("SdkDepsMutator", sdkDepsMutator).Parallel()
+ ctx.BottomUp("SdkDepsReplaceMutator", sdkDepsReplaceMutator).Parallel()
+ ctx.TopDown("SdkRequirementCheck", sdkRequirementsMutator).Parallel()
+}
+
+type dependencyTag struct {
+ blueprint.BaseDependencyTag
+}
+
+// For dependencies from an in-development version of an SDK member to frozen versions of the same member
+// e.g. libfoo -> libfoo.mysdk.11 and libfoo.mysdk.12
+type sdkMemberVersionedDepTag struct {
+ dependencyTag
+ member string
+ version string
+}
+
+// Mark this tag so dependencies that use it are excluded from visibility enforcement.
+func (t sdkMemberVersionedDepTag) ExcludeFromVisibilityEnforcement() {}
+
+// Step 1: create dependencies from an SDK module to its members.
+func memberMutator(mctx android.BottomUpMutatorContext) {
+ if s, ok := mctx.Module().(*sdk); ok {
+ // Add dependencies from enabled and non CommonOS variants to the sdk member variants.
+ if s.Enabled() && !s.IsCommonOSVariant() {
+ for _, memberListProperty := range s.memberListProperties() {
+ names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
+ if len(names) > 0 {
+ tag := memberListProperty.dependencyTag
+ memberListProperty.memberType.AddDependencies(mctx, tag, names)
+ }
+ }
+ }
+ }
+}
+
+// Step 2: record that dependencies of SDK modules are members of the SDK modules
+func memberDepsMutator(mctx android.TopDownMutatorContext) {
+ if s, ok := mctx.Module().(*sdk); ok {
+ mySdkRef := android.ParseSdkRef(mctx, mctx.ModuleName(), "name")
+ if s.snapshot() && mySdkRef.Unversioned() {
+ mctx.PropertyErrorf("name", "sdk_snapshot should be named as <name>@<version>. "+
+ "Did you manually modify Android.bp?")
+ }
+ if !s.snapshot() && !mySdkRef.Unversioned() {
+ mctx.PropertyErrorf("name", "sdk shouldn't be named as <name>@<version>.")
+ }
+ if mySdkRef.Version != "" && mySdkRef.Version != "current" {
+ if _, err := strconv.Atoi(mySdkRef.Version); err != nil {
+ mctx.PropertyErrorf("name", "version %q is neither a number nor \"current\"", mySdkRef.Version)
+ }
+ }
+
+ mctx.VisitDirectDeps(func(child android.Module) {
+ if member, ok := child.(android.SdkAware); ok {
+ member.MakeMemberOf(mySdkRef)
+ }
+ })
+ }
+}
+
+// Step 3: create dependencies from the unversioned SDK member to snapshot versions
+// of the same member. By having these dependencies, they are mutated for multiple Mainline modules
+// (apex and apk), each of which might want different sdks to be built with. For example, if both
+// apex A and B are referencing libfoo which is a member of sdk 'mysdk', the two APEXes can be
+// built with libfoo.mysdk.11 and libfoo.mysdk.12, respectively depending on which sdk they are
+// using.
+func memberInterVersionMutator(mctx android.BottomUpMutatorContext) {
+ if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() {
+ if !m.ContainingSdk().Unversioned() {
+ memberName := m.MemberName()
+ tag := sdkMemberVersionedDepTag{member: memberName, version: m.ContainingSdk().Version}
+ mctx.AddReverseDependency(mctx.Module(), tag, memberName)
+ }
+ }
+}
+
+// Step 4: transitively ripple down the SDK requirements from the root modules like APEX to its
+// descendants
+func sdkDepsMutator(mctx android.TopDownMutatorContext) {
+ if m, ok := mctx.Module().(android.SdkAware); ok {
+ // Module types for Mainline modules (e.g. APEX) are expected to implement RequiredSdks()
+ // by reading its own properties like `uses_sdks`.
+ requiredSdks := m.RequiredSdks()
+ if len(requiredSdks) > 0 {
+ mctx.VisitDirectDeps(func(m android.Module) {
+ if dep, ok := m.(android.SdkAware); ok {
+ dep.BuildWithSdks(requiredSdks)
+ }
+ })
+ }
+ }
+}
+
+// Step 5: if libfoo.mysdk.11 is in the context where version 11 of mysdk is requested, the
+// versioned module is used instead of the un-versioned (in-development) module libfoo
+func sdkDepsReplaceMutator(mctx android.BottomUpMutatorContext) {
+ if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() {
+ if sdk := m.ContainingSdk(); !sdk.Unversioned() {
+ if m.RequiredSdks().Contains(sdk) {
+ // Note that this replacement is done only for the modules that have the same
+ // variations as the current module. Since current module is already mutated for
+ // apex references in other APEXes are not affected by this replacement.
+ memberName := m.MemberName()
+ mctx.ReplaceDependencies(memberName)
+ }
+ }
+ }
+}
+
+// Step 6: ensure that the dependencies outside of the APEX are all from the required SDKs
+func sdkRequirementsMutator(mctx android.TopDownMutatorContext) {
+ if m, ok := mctx.Module().(interface {
+ android.DepIsInSameApex
+ android.RequiredSdks
+ }); ok {
+ requiredSdks := m.RequiredSdks()
+ if len(requiredSdks) == 0 {
+ return
+ }
+ mctx.VisitDirectDeps(func(dep android.Module) {
+ tag := mctx.OtherModuleDependencyTag(dep)
+ if tag == android.DefaultsDepTag {
+ // dependency to defaults is always okay
+ return
+ }
+
+ // Ignore the dependency from the unversioned member to any versioned members as an
+ // apex that depends on the unversioned member will not also be depending on a versioned
+ // member.
+ if _, ok := tag.(sdkMemberVersionedDepTag); ok {
+ return
+ }
+
+ // If the dep is outside of the APEX, but is not in any of the
+ // required SDKs, we know that the dep is a violation.
+ if sa, ok := dep.(android.SdkAware); ok {
+ if !m.DepIsInSameApex(mctx, dep) && !requiredSdks.Contains(sa.ContainingSdk()) {
+ mctx.ModuleErrorf("depends on %q (in SDK %q) that isn't part of the required SDKs: %v",
+ sa.Name(), sa.ContainingSdk(), requiredSdks)
+ }
+ }
+ })
+ }
+}
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
new file mode 100644
index 000000000..56be7417b
--- /dev/null
+++ b/sdk/sdk_test.go
@@ -0,0 +1,410 @@
+// Copyright 2019 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 sdk
+
+import (
+ "testing"
+
+ "github.com/google/blueprint/proptools"
+)
+
+// Needed in an _test.go file in this package to ensure tests run correctly, particularly in IDE.
+func TestMain(m *testing.M) {
+ runTestWithBuildDir(m)
+}
+
+func TestDepNotInRequiredSdks(t *testing.T) {
+ testSdkError(t, `module "myjavalib".*depends on "otherlib".*that isn't part of the required SDKs:.*`, `
+ sdk {
+ name: "mysdk",
+ java_header_libs: ["sdkmember"],
+ }
+
+ sdk_snapshot {
+ name: "mysdk@1",
+ java_header_libs: ["sdkmember_mysdk_1"],
+ }
+
+ java_import {
+ name: "sdkmember",
+ prefer: false,
+ host_supported: true,
+ }
+
+ java_import {
+ name: "sdkmember_mysdk_1",
+ sdk_member_name: "sdkmember",
+ host_supported: true,
+ }
+
+ java_library {
+ name: "myjavalib",
+ srcs: ["Test.java"],
+ libs: [
+ "sdkmember",
+ "otherlib",
+ ],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ host_supported: true,
+ apex_available: ["myapex"],
+ }
+
+ // this lib is no in mysdk
+ java_library {
+ name: "otherlib",
+ srcs: ["Test.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ host_supported: true,
+ }
+
+ apex {
+ name: "myapex",
+ java_libs: ["myjavalib"],
+ uses_sdks: ["mysdk@1"],
+ key: "myapex.key",
+ certificate: ":myapex.cert",
+ }
+ `)
+}
+
+// Ensure that prebuilt modules have the same effective visibility as the source
+// modules.
+func TestSnapshotVisibility(t *testing.T) {
+ packageBp := `
+ package {
+ default_visibility: ["//other/foo"],
+ }
+
+ sdk {
+ name: "mysdk",
+ visibility: [
+ "//other/foo",
+ // This short form will be replaced with //package:__subpackages__ in the
+ // generated sdk_snapshot.
+ ":__subpackages__",
+ ],
+ java_header_libs: [
+ "myjavalib",
+ "mypublicjavalib",
+ "mydefaultedjavalib",
+ "myprivatejavalib",
+ ],
+ }
+
+ java_library {
+ name: "myjavalib",
+ // Uses package default visibility
+ srcs: ["Test.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ }
+
+ java_defaults {
+ name: "java-defaults",
+ visibility: ["//other/bar"],
+ }
+
+ java_library {
+ name: "mypublicjavalib",
+ defaults: ["java-defaults"],
+ visibility: ["//visibility:public"],
+ srcs: ["Test.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ }
+
+ java_defaults {
+ name: "myjavadefaults",
+ visibility: ["//other/bar"],
+ }
+
+ java_library {
+ name: "mydefaultedjavalib",
+ defaults: ["myjavadefaults"],
+ srcs: ["Test.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ }
+
+ java_library {
+ name: "myprivatejavalib",
+ srcs: ["Test.java"],
+ visibility: ["//visibility:private"],
+ system_modules: "none",
+ sdk_version: "none",
+ }
+ `
+
+ result := testSdkWithFs(t, ``,
+ map[string][]byte{
+ "package/Test.java": nil,
+ "package/Android.bp": []byte(packageBp),
+ })
+
+ result.CheckSnapshot("mysdk", "package",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ visibility: [
+ "//other/foo",
+ "//package",
+ ],
+ jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ visibility: [
+ "//other/foo",
+ "//package",
+ ],
+ jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+ name: "mysdk_mypublicjavalib@current",
+ sdk_member_name: "mypublicjavalib",
+ visibility: ["//visibility:public"],
+ jars: ["java/mypublicjavalib.jar"],
+}
+
+java_import {
+ name: "mypublicjavalib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ jars: ["java/mypublicjavalib.jar"],
+}
+
+java_import {
+ name: "mysdk_mydefaultedjavalib@current",
+ sdk_member_name: "mydefaultedjavalib",
+ visibility: [
+ "//other/bar",
+ "//package",
+ ],
+ jars: ["java/mydefaultedjavalib.jar"],
+}
+
+java_import {
+ name: "mydefaultedjavalib",
+ prefer: false,
+ visibility: [
+ "//other/bar",
+ "//package",
+ ],
+ jars: ["java/mydefaultedjavalib.jar"],
+}
+
+java_import {
+ name: "mysdk_myprivatejavalib@current",
+ sdk_member_name: "myprivatejavalib",
+ visibility: ["//package"],
+ jars: ["java/myprivatejavalib.jar"],
+}
+
+java_import {
+ name: "myprivatejavalib",
+ prefer: false,
+ visibility: ["//package"],
+ jars: ["java/myprivatejavalib.jar"],
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ visibility: [
+ "//other/foo",
+ "//package:__subpackages__",
+ ],
+ java_header_libs: [
+ "mysdk_myjavalib@current",
+ "mysdk_mypublicjavalib@current",
+ "mysdk_mydefaultedjavalib@current",
+ "mysdk_myprivatejavalib@current",
+ ],
+}
+`))
+}
+
+func TestSDkInstall(t *testing.T) {
+ sdk := `
+ sdk {
+ name: "mysdk",
+ }
+ `
+ result := testSdkWithFs(t, ``,
+ map[string][]byte{
+ "Android.bp": []byte(sdk),
+ })
+
+ result.CheckSnapshot("mysdk", "",
+ checkAllOtherCopyRules(`.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip`),
+ )
+}
+
+type EmbeddedPropertiesStruct struct {
+ S_Embedded_Common string `android:"arch_variant"`
+ S_Embedded_Different string `android:"arch_variant"`
+}
+
+type testPropertiesStruct struct {
+ name string
+ private string
+ Public_Kept string `sdk:"keep"`
+ S_Common string
+ S_Different string `android:"arch_variant"`
+ A_Common []string
+ A_Different []string `android:"arch_variant"`
+ F_Common *bool
+ F_Different *bool `android:"arch_variant"`
+ EmbeddedPropertiesStruct
+}
+
+func (p *testPropertiesStruct) optimizableProperties() interface{} {
+ return p
+}
+
+func (p *testPropertiesStruct) String() string {
+ return p.name
+}
+
+var _ propertiesContainer = (*testPropertiesStruct)(nil)
+
+func TestCommonValueOptimization(t *testing.T) {
+ common := &testPropertiesStruct{name: "common"}
+ structs := []propertiesContainer{
+ &testPropertiesStruct{
+ name: "struct-0",
+ private: "common",
+ Public_Kept: "common",
+ S_Common: "common",
+ S_Different: "upper",
+ A_Common: []string{"first", "second"},
+ A_Different: []string{"alpha", "beta"},
+ F_Common: proptools.BoolPtr(false),
+ F_Different: proptools.BoolPtr(false),
+ EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
+ S_Embedded_Common: "embedded_common",
+ S_Embedded_Different: "embedded_upper",
+ },
+ },
+ &testPropertiesStruct{
+ name: "struct-1",
+ private: "common",
+ Public_Kept: "common",
+ S_Common: "common",
+ S_Different: "lower",
+ A_Common: []string{"first", "second"},
+ A_Different: []string{"alpha", "delta"},
+ F_Common: proptools.BoolPtr(false),
+ F_Different: proptools.BoolPtr(true),
+ EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
+ S_Embedded_Common: "embedded_common",
+ S_Embedded_Different: "embedded_lower",
+ },
+ },
+ }
+
+ extractor := newCommonValueExtractor(common)
+
+ h := TestHelper{t}
+
+ err := extractor.extractCommonProperties(common, structs)
+ h.AssertDeepEquals("unexpected error", nil, err)
+
+ h.AssertDeepEquals("common properties not correct",
+ &testPropertiesStruct{
+ name: "common",
+ private: "",
+ Public_Kept: "",
+ S_Common: "common",
+ S_Different: "",
+ A_Common: []string{"first", "second"},
+ A_Different: []string(nil),
+ F_Common: proptools.BoolPtr(false),
+ F_Different: nil,
+ EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
+ S_Embedded_Common: "embedded_common",
+ S_Embedded_Different: "",
+ },
+ },
+ common)
+
+ h.AssertDeepEquals("updated properties[0] not correct",
+ &testPropertiesStruct{
+ name: "struct-0",
+ private: "common",
+ Public_Kept: "common",
+ S_Common: "",
+ S_Different: "upper",
+ A_Common: nil,
+ A_Different: []string{"alpha", "beta"},
+ F_Common: nil,
+ F_Different: proptools.BoolPtr(false),
+ EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
+ S_Embedded_Common: "",
+ S_Embedded_Different: "embedded_upper",
+ },
+ },
+ structs[0])
+
+ h.AssertDeepEquals("updated properties[1] not correct",
+ &testPropertiesStruct{
+ name: "struct-1",
+ private: "common",
+ Public_Kept: "common",
+ S_Common: "",
+ S_Different: "lower",
+ A_Common: nil,
+ A_Different: []string{"alpha", "delta"},
+ F_Common: nil,
+ F_Different: proptools.BoolPtr(true),
+ EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
+ S_Embedded_Common: "",
+ S_Embedded_Different: "embedded_lower",
+ },
+ },
+ structs[1])
+}
+
+func TestCommonValueOptimization_InvalidArchSpecificVariants(t *testing.T) {
+ common := &testPropertiesStruct{name: "common"}
+ structs := []propertiesContainer{
+ &testPropertiesStruct{
+ name: "struct-0",
+ S_Common: "should-be-but-is-not-common0",
+ },
+ &testPropertiesStruct{
+ name: "struct-1",
+ S_Common: "should-be-but-is-not-common1",
+ },
+ }
+
+ extractor := newCommonValueExtractor(common)
+
+ h := TestHelper{t}
+
+ err := extractor.extractCommonProperties(common, structs)
+ h.AssertErrorMessageEquals("unexpected error", `field "S_Common" is not tagged as "arch_variant" but has arch specific properties:
+ "struct-0" has value "should-be-but-is-not-common0"
+ "struct-1" has value "should-be-but-is-not-common1"`, err)
+}
diff --git a/sdk/testing.go b/sdk/testing.go
new file mode 100644
index 000000000..436175419
--- /dev/null
+++ b/sdk/testing.go
@@ -0,0 +1,429 @@
+// Copyright 2019 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 sdk
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "reflect"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+ "android/soong/apex"
+ "android/soong/cc"
+ "android/soong/java"
+)
+
+func testSdkContext(bp string, fs map[string][]byte) (*android.TestContext, android.Config) {
+ bp = bp + `
+ apex_key {
+ name: "myapex.key",
+ public_key: "myapex.avbpubkey",
+ private_key: "myapex.pem",
+ }
+
+ android_app_certificate {
+ name: "myapex.cert",
+ certificate: "myapex",
+ }
+ ` + cc.GatherRequiredDepsForTest(android.Android, android.Windows)
+
+ mockFS := map[string][]byte{
+ "build/make/target/product/security": nil,
+ "apex_manifest.json": nil,
+ "system/sepolicy/apex/myapex-file_contexts": nil,
+ "system/sepolicy/apex/myapex2-file_contexts": nil,
+ "myapex.avbpubkey": nil,
+ "myapex.pem": nil,
+ "myapex.x509.pem": nil,
+ "myapex.pk8": nil,
+ }
+
+ cc.GatherRequiredFilesForTest(mockFS)
+
+ for k, v := range fs {
+ mockFS[k] = v
+ }
+
+ config := android.TestArchConfig(buildDir, nil, bp, mockFS)
+
+ // Add windows as a default disable OS to test behavior when some OS variants
+ // are disabled.
+ config.Targets[android.Windows] = []android.Target{
+ {android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", ""},
+ }
+
+ ctx := android.NewTestArchContext()
+
+ // Enable androidmk support.
+ // * Register the singleton
+ // * Configure that we are inside make
+ // * Add CommonOS to ensure that androidmk processing works.
+ android.RegisterAndroidMkBuildComponents(ctx)
+ android.SetInMakeForTests(config)
+ config.Targets[android.CommonOS] = []android.Target{
+ {android.CommonOS, android.Arch{ArchType: android.Common}, android.NativeBridgeDisabled, "", ""},
+ }
+
+ // from android package
+ android.RegisterPackageBuildComponents(ctx)
+ ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
+ ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+ ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
+ ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
+
+ // from java package
+ java.RegisterJavaBuildComponents(ctx)
+ java.RegisterAppBuildComponents(ctx)
+ java.RegisterSdkLibraryBuildComponents(ctx)
+ java.RegisterStubsBuildComponents(ctx)
+ java.RegisterSystemModulesBuildComponents(ctx)
+
+ // from cc package
+ cc.RegisterRequiredBuildComponentsForTest(ctx)
+
+ // from apex package
+ ctx.RegisterModuleType("apex", apex.BundleFactory)
+ ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory)
+ ctx.PostDepsMutators(apex.RegisterPostDepsMutators)
+
+ // from this package
+ ctx.RegisterModuleType("sdk", SdkModuleFactory)
+ ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
+ ctx.RegisterModuleType("module_exports", ModuleExportsFactory)
+ ctx.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory)
+ ctx.PreDepsMutators(RegisterPreDepsMutators)
+ ctx.PostDepsMutators(RegisterPostDepsMutators)
+
+ ctx.Register(config)
+
+ return ctx, config
+}
+
+func testSdkWithFs(t *testing.T, bp string, fs map[string][]byte) *testSdkResult {
+ t.Helper()
+ ctx, config := testSdkContext(bp, fs)
+ _, errs := ctx.ParseBlueprintsFiles(".")
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+ return &testSdkResult{
+ TestHelper: TestHelper{t: t},
+ ctx: ctx,
+ config: config,
+ }
+}
+
+func testSdkError(t *testing.T, pattern, bp string) {
+ t.Helper()
+ ctx, config := testSdkContext(bp, nil)
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ if len(errs) > 0 {
+ android.FailIfNoMatchingErrors(t, pattern, errs)
+ return
+ }
+ _, errs = ctx.PrepareBuildActions(config)
+ if len(errs) > 0 {
+ android.FailIfNoMatchingErrors(t, pattern, errs)
+ return
+ }
+
+ t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+}
+
+func ensureListContains(t *testing.T, result []string, expected string) {
+ t.Helper()
+ if !android.InList(expected, result) {
+ t.Errorf("%q is not found in %v", expected, result)
+ }
+}
+
+func pathsToStrings(paths android.Paths) []string {
+ var ret []string
+ for _, p := range paths {
+ ret = append(ret, p.String())
+ }
+ return ret
+}
+
+// Provides general test support.
+type TestHelper struct {
+ t *testing.T
+}
+
+func (h *TestHelper) AssertStringEquals(message string, expected string, actual string) {
+ h.t.Helper()
+ if actual != expected {
+ h.t.Errorf("%s: expected %s, actual %s", message, expected, actual)
+ }
+}
+
+func (h *TestHelper) AssertErrorMessageEquals(message string, expected string, actual error) {
+ h.t.Helper()
+ if actual == nil {
+ h.t.Errorf("Expected error but was nil")
+ } else if actual.Error() != expected {
+ h.t.Errorf("%s: expected %s, actual %s", message, expected, actual.Error())
+ }
+}
+
+func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) {
+ h.t.Helper()
+ h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual))
+}
+
+func (h *TestHelper) AssertDeepEquals(message string, expected interface{}, actual interface{}) {
+ h.t.Helper()
+ if !reflect.DeepEqual(actual, expected) {
+ h.t.Errorf("%s: expected %#v, actual %#v", message, expected, actual)
+ }
+}
+
+// Encapsulates result of processing an SDK definition. Provides support for
+// checking the state of the build structures.
+type testSdkResult struct {
+ TestHelper
+ ctx *android.TestContext
+ config android.Config
+}
+
+// Analyse the sdk build rules to extract information about what it is doing.
+
+// e.g. find the src/dest pairs from each cp command, the various zip files
+// generated, etc.
+func (r *testSdkResult) getSdkSnapshotBuildInfo(sdk *sdk) *snapshotBuildInfo {
+ androidBpContents := sdk.GetAndroidBpContentsForTests()
+
+ info := &snapshotBuildInfo{
+ r: r,
+ androidBpContents: androidBpContents,
+ }
+
+ buildParams := sdk.BuildParamsForTests()
+ copyRules := &strings.Builder{}
+ otherCopyRules := &strings.Builder{}
+ snapshotDirPrefix := sdk.builderForTests.snapshotDir.String() + "/"
+ for _, bp := range buildParams {
+ switch bp.Rule.String() {
+ case android.Cp.String():
+ output := bp.Output
+ // Get destination relative to the snapshot root
+ dest := output.Rel()
+ src := android.NormalizePathForTesting(bp.Input)
+ // We differentiate between copy rules for the snapshot, and copy rules for the install file.
+ if strings.HasPrefix(output.String(), snapshotDirPrefix) {
+ // Get source relative to build directory.
+ _, _ = fmt.Fprintf(copyRules, "%s -> %s\n", src, dest)
+ info.snapshotContents = append(info.snapshotContents, dest)
+ } else {
+ _, _ = fmt.Fprintf(otherCopyRules, "%s -> %s\n", src, dest)
+ }
+
+ case repackageZip.String():
+ // Add the destdir to the snapshot contents as that is effectively where
+ // the content of the repackaged zip is copied.
+ dest := bp.Args["destdir"]
+ info.snapshotContents = append(info.snapshotContents, dest)
+
+ case zipFiles.String():
+ // This could be an intermediate zip file and not the actual output zip.
+ // In that case this will be overridden when the rule to merge the zips
+ // is processed.
+ info.outputZip = android.NormalizePathForTesting(bp.Output)
+
+ case mergeZips.String():
+ // Copy the current outputZip to the intermediateZip.
+ info.intermediateZip = info.outputZip
+ mergeInput := android.NormalizePathForTesting(bp.Input)
+ if info.intermediateZip != mergeInput {
+ r.t.Errorf("Expected intermediate zip %s to be an input to merge zips but found %s instead",
+ info.intermediateZip, mergeInput)
+ }
+
+ // Override output zip (which was actually the intermediate zip file) with the actual
+ // output zip.
+ info.outputZip = android.NormalizePathForTesting(bp.Output)
+
+ // Save the zips to be merged into the intermediate zip.
+ info.mergeZips = android.NormalizePathsForTesting(bp.Inputs)
+ }
+ }
+
+ info.copyRules = copyRules.String()
+ info.otherCopyRules = otherCopyRules.String()
+
+ return info
+}
+
+func (r *testSdkResult) Module(name string, variant string) android.Module {
+ return r.ctx.ModuleForTests(name, variant).Module()
+}
+
+func (r *testSdkResult) ModuleForTests(name string, variant string) android.TestingModule {
+ return r.ctx.ModuleForTests(name, variant)
+}
+
+// Check the snapshot build rules.
+//
+// Takes a list of functions which check different facets of the snapshot build rules.
+// Allows each test to customize what is checked without duplicating lots of code
+// or proliferating check methods of different flavors.
+func (r *testSdkResult) CheckSnapshot(name string, dir string, checkers ...snapshotBuildInfoChecker) {
+ r.t.Helper()
+
+ // The sdk CommonOS variant is always responsible for generating the snapshot.
+ variant := android.CommonOS.Name
+
+ sdk := r.Module(name, variant).(*sdk)
+
+ snapshotBuildInfo := r.getSdkSnapshotBuildInfo(sdk)
+
+ // Check state of the snapshot build.
+ for _, checker := range checkers {
+ checker(snapshotBuildInfo)
+ }
+
+ // Make sure that the generated zip file is in the correct place.
+ actual := snapshotBuildInfo.outputZip
+ if dir != "" {
+ dir = filepath.Clean(dir) + "/"
+ }
+ r.AssertStringEquals("Snapshot zip file in wrong place",
+ fmt.Sprintf(".intermediates/%s%s/%s/%s-current.zip", dir, name, variant, name), actual)
+
+ // Populate a mock filesystem with the files that would have been copied by
+ // the rules.
+ fs := make(map[string][]byte)
+ for _, dest := range snapshotBuildInfo.snapshotContents {
+ fs[dest] = nil
+ }
+
+ // Process the generated bp file to make sure it is valid.
+ testSdkWithFs(r.t, snapshotBuildInfo.androidBpContents, fs)
+}
+
+type snapshotBuildInfoChecker func(info *snapshotBuildInfo)
+
+// Check that the snapshot's generated Android.bp is correct.
+//
+// Both the expected and actual string are both trimmed before comparing.
+func checkAndroidBpContents(expected string) snapshotBuildInfoChecker {
+ return func(info *snapshotBuildInfo) {
+ info.r.t.Helper()
+ info.r.AssertTrimmedStringEquals("Android.bp contents do not match", expected, info.androidBpContents)
+ }
+}
+
+// Check that the snapshot's copy rules are correct.
+//
+// The copy rules are formatted as <src> -> <dest>, one per line and then compared
+// to the supplied expected string. Both the expected and actual string are trimmed
+// before comparing.
+func checkAllCopyRules(expected string) snapshotBuildInfoChecker {
+ return func(info *snapshotBuildInfo) {
+ info.r.t.Helper()
+ info.r.AssertTrimmedStringEquals("Incorrect copy rules", expected, info.copyRules)
+ }
+}
+
+func checkAllOtherCopyRules(expected string) snapshotBuildInfoChecker {
+ return func(info *snapshotBuildInfo) {
+ info.r.t.Helper()
+ info.r.AssertTrimmedStringEquals("Incorrect copy rules", expected, info.otherCopyRules)
+ }
+}
+
+// Check that the specified paths match the list of zips to merge with the intermediate zip.
+func checkMergeZips(expected ...string) snapshotBuildInfoChecker {
+ return func(info *snapshotBuildInfo) {
+ info.r.t.Helper()
+ if info.intermediateZip == "" {
+ info.r.t.Errorf("No intermediate zip file was created")
+ }
+
+ info.r.AssertDeepEquals("mismatching merge zip files", expected, info.mergeZips)
+ }
+}
+
+// Encapsulates information about the snapshot build structure in order to insulate tests from
+// knowing too much about internal structures.
+//
+// All source/input paths are relative either the build directory. All dest/output paths are
+// relative to the snapshot root directory.
+type snapshotBuildInfo struct {
+ r *testSdkResult
+
+ // The contents of the generated Android.bp file
+ androidBpContents string
+
+ // The paths, relative to the snapshot root, of all files and directories copied into the
+ // snapshot.
+ snapshotContents []string
+
+ // A formatted representation of the src/dest pairs for a snapshot, one pair per line,
+ // of the format src -> dest
+ copyRules string
+
+ // A formatted representation of the src/dest pairs for files not in a snapshot, one pair
+ // per line, of the format src -> dest
+ otherCopyRules string
+
+ // The path to the intermediate zip, which is a zip created from the source files copied
+ // into the snapshot directory and which will be merged with other zips to form the final output.
+ // Is am empty string if there is no intermediate zip because there are no zips to merge in.
+ intermediateZip string
+
+ // The paths to the zips to merge into the output zip, does not include the intermediate
+ // zip.
+ mergeZips []string
+
+ // The final output zip.
+ outputZip string
+}
+
+var buildDir string
+
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "soong_sdk_test")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func tearDown() {
+ _ = os.RemoveAll(buildDir)
+}
+
+func runTestWithBuildDir(m *testing.M) {
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
+}
+
+func SkipIfNotLinux(t *testing.T) {
+ t.Helper()
+ if android.BuildOs != android.Linux {
+ t.Skipf("Skipping as sdk snapshot generation is only supported on %s not %s", android.Linux, android.BuildOs)
+ }
+}
diff --git a/sdk/update.go b/sdk/update.go
new file mode 100644
index 000000000..59a764001
--- /dev/null
+++ b/sdk/update.go
@@ -0,0 +1,1494 @@
+// Copyright (C) 2019 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.
+
+package sdk
+
+import (
+ "fmt"
+ "reflect"
+ "sort"
+ "strings"
+
+ "android/soong/apex"
+ "android/soong/cc"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+var pctx = android.NewPackageContext("android/soong/sdk")
+
+var (
+ repackageZip = pctx.AndroidStaticRule("SnapshotRepackageZip",
+ blueprint.RuleParams{
+ Command: `${config.Zip2ZipCmd} -i $in -o $out -x META-INF/**/* "**/*:$destdir"`,
+ CommandDeps: []string{
+ "${config.Zip2ZipCmd}",
+ },
+ },
+ "destdir")
+
+ zipFiles = pctx.AndroidStaticRule("SnapshotZipFiles",
+ blueprint.RuleParams{
+ Command: `${config.SoongZipCmd} -C $basedir -l $out.rsp -o $out`,
+ CommandDeps: []string{
+ "${config.SoongZipCmd}",
+ },
+ Rspfile: "$out.rsp",
+ RspfileContent: "$in",
+ },
+ "basedir")
+
+ mergeZips = pctx.AndroidStaticRule("SnapshotMergeZips",
+ blueprint.RuleParams{
+ Command: `${config.MergeZipsCmd} $out $in`,
+ CommandDeps: []string{
+ "${config.MergeZipsCmd}",
+ },
+ })
+)
+
+type generatedContents struct {
+ content strings.Builder
+ indentLevel int
+}
+
+// generatedFile abstracts operations for writing contents into a file and emit a build rule
+// for the file.
+type generatedFile struct {
+ generatedContents
+ path android.OutputPath
+}
+
+func newGeneratedFile(ctx android.ModuleContext, path ...string) *generatedFile {
+ return &generatedFile{
+ path: android.PathForModuleOut(ctx, path...).OutputPath,
+ }
+}
+
+func (gc *generatedContents) Indent() {
+ gc.indentLevel++
+}
+
+func (gc *generatedContents) Dedent() {
+ gc.indentLevel--
+}
+
+func (gc *generatedContents) Printfln(format string, args ...interface{}) {
+ fmt.Fprintf(&(gc.content), strings.Repeat(" ", gc.indentLevel)+format+"\n", args...)
+}
+
+func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) {
+ rb := android.NewRuleBuilder()
+
+ content := gf.content.String()
+
+ // ninja consumes newline characters in rspfile_content. Prevent it by
+ // escaping the backslash in the newline character. The extra backslash
+ // is removed when the rspfile is written to the actual script file
+ content = strings.ReplaceAll(content, "\n", "\\n")
+
+ rb.Command().
+ Implicits(implicits).
+ Text("echo").Text(proptools.ShellEscape(content)).
+ // convert \\n to \n
+ Text("| sed 's/\\\\n/\\n/g' >").Output(gf.path)
+ rb.Command().
+ Text("chmod a+x").Output(gf.path)
+ rb.Build(pctx, ctx, gf.path.Base(), "Build "+gf.path.Base())
+}
+
+// Collect all the members.
+//
+// Returns a list containing type (extracted from the dependency tag) and the variant
+// plus the multilib usages.
+func (s *sdk) collectMembers(ctx android.ModuleContext) {
+ s.multilibUsages = multilibNone
+ ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
+ tag := ctx.OtherModuleDependencyTag(child)
+ if memberTag, ok := tag.(android.SdkMemberTypeDependencyTag); ok {
+ memberType := memberTag.SdkMemberType()
+
+ // Make sure that the resolved module is allowed in the member list property.
+ if !memberType.IsInstance(child) {
+ ctx.ModuleErrorf("module %q is not valid in property %s", ctx.OtherModuleName(child), memberType.SdkPropertyName())
+ }
+
+ // Keep track of which multilib variants are used by the sdk.
+ s.multilibUsages = s.multilibUsages.addArchType(child.Target().Arch.ArchType)
+
+ s.memberRefs = append(s.memberRefs, sdkMemberRef{memberType, child.(android.SdkAware)})
+
+ // If the member type supports transitive sdk members then recurse down into
+ // its dependencies, otherwise exit traversal.
+ return memberType.HasTransitiveSdkMembers()
+ }
+
+ return false
+ })
+}
+
+// Organize the members.
+//
+// The members are first grouped by type and then grouped by name. The order of
+// the types is the order they are referenced in android.SdkMemberTypesRegistry.
+// The names are in the order in which the dependencies were added.
+//
+// Returns the members as well as the multilib setting to use.
+func (s *sdk) organizeMembers(ctx android.ModuleContext, memberRefs []sdkMemberRef) []*sdkMember {
+ byType := make(map[android.SdkMemberType][]*sdkMember)
+ byName := make(map[string]*sdkMember)
+
+ for _, memberRef := range memberRefs {
+ memberType := memberRef.memberType
+ variant := memberRef.variant
+
+ name := ctx.OtherModuleName(variant)
+ member := byName[name]
+ if member == nil {
+ member = &sdkMember{memberType: memberType, name: name}
+ byName[name] = member
+ byType[memberType] = append(byType[memberType], member)
+ }
+
+ // Only append new variants to the list. This is needed because a member can be both
+ // exported by the sdk and also be a transitive sdk member.
+ member.variants = appendUniqueVariants(member.variants, variant)
+ }
+
+ var members []*sdkMember
+ for _, memberListProperty := range s.memberListProperties() {
+ membersOfType := byType[memberListProperty.memberType]
+ members = append(members, membersOfType...)
+ }
+
+ return members
+}
+
+func appendUniqueVariants(variants []android.SdkAware, newVariant android.SdkAware) []android.SdkAware {
+ for _, v := range variants {
+ if v == newVariant {
+ return variants
+ }
+ }
+ return append(variants, newVariant)
+}
+
+// SDK directory structure
+// <sdk_root>/
+// Android.bp : definition of a 'sdk' module is here. This is a hand-made one.
+// <api_ver>/ : below this directory are all auto-generated
+// Android.bp : definition of 'sdk_snapshot' module is here
+// aidl/
+// frameworks/base/core/..../IFoo.aidl : an exported AIDL file
+// java/
+// <module_name>.jar : the stub jar for a java library 'module_name'
+// include/
+// bionic/libc/include/stdlib.h : an exported header file
+// include_gen/
+// <module_name>/com/android/.../IFoo.h : a generated header file
+// <arch>/include/ : arch-specific exported headers
+// <arch>/include_gen/ : arch-specific generated headers
+// <arch>/lib/
+// libFoo.so : a stub library
+
+// A name that uniquely identifies a prebuilt SDK member for a version of SDK snapshot
+// This isn't visible to users, so could be changed in future.
+func versionedSdkMemberName(ctx android.ModuleContext, memberName string, version string) string {
+ return ctx.ModuleName() + "_" + memberName + string(android.SdkVersionSeparator) + version
+}
+
+// buildSnapshot is the main function in this source file. It creates rules to copy
+// the contents (header files, stub libraries, etc) into the zip file.
+func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) android.OutputPath {
+
+ allMembersByName := make(map[string]struct{})
+ exportedMembersByName := make(map[string]struct{})
+ var memberRefs []sdkMemberRef
+ for _, sdkVariant := range sdkVariants {
+ memberRefs = append(memberRefs, sdkVariant.memberRefs...)
+
+ // Record the names of all the members, both explicitly specified and implicitly
+ // included.
+ for _, memberRef := range sdkVariant.memberRefs {
+ allMembersByName[memberRef.variant.Name()] = struct{}{}
+ }
+
+ // Merge the exported member sets from all sdk variants.
+ for key, _ := range sdkVariant.getExportedMembers() {
+ exportedMembersByName[key] = struct{}{}
+ }
+ }
+
+ snapshotDir := android.PathForModuleOut(ctx, "snapshot")
+
+ bp := newGeneratedFile(ctx, "snapshot", "Android.bp")
+
+ bpFile := &bpFile{
+ modules: make(map[string]*bpModule),
+ }
+
+ builder := &snapshotBuilder{
+ ctx: ctx,
+ sdk: s,
+ version: "current",
+ snapshotDir: snapshotDir.OutputPath,
+ copies: make(map[string]string),
+ filesToZip: []android.Path{bp.path},
+ bpFile: bpFile,
+ prebuiltModules: make(map[string]*bpModule),
+ allMembersByName: allMembersByName,
+ exportedMembersByName: exportedMembersByName,
+ }
+ s.builderForTests = builder
+
+ members := s.organizeMembers(ctx, memberRefs)
+ for _, member := range members {
+ memberType := member.memberType
+
+ memberCtx := &memberContext{ctx, builder, memberType, member.name}
+
+ prebuiltModule := memberType.AddPrebuiltModule(memberCtx, member)
+ s.createMemberSnapshot(memberCtx, member, prebuiltModule)
+ }
+
+ // Create a transformer that will transform an unversioned module into a versioned module.
+ unversionedToVersionedTransformer := unversionedToVersionedTransformation{builder: builder}
+
+ // Create a transformer that will transform an unversioned module by replacing any references
+ // to internal members with a unique module name and setting prefer: false.
+ unversionedTransformer := unversionedTransformation{builder: builder}
+
+ for _, unversioned := range builder.prebuiltOrder {
+ // Prune any empty property sets.
+ unversioned = unversioned.transform(pruneEmptySetTransformer{})
+
+ // Copy the unversioned module so it can be modified to make it versioned.
+ versioned := unversioned.deepCopy()
+
+ // Transform the unversioned module into a versioned one.
+ versioned.transform(unversionedToVersionedTransformer)
+ bpFile.AddModule(versioned)
+
+ // Transform the unversioned module to make it suitable for use in the snapshot.
+ unversioned.transform(unversionedTransformer)
+ bpFile.AddModule(unversioned)
+ }
+
+ // Create the snapshot module.
+ snapshotName := ctx.ModuleName() + string(android.SdkVersionSeparator) + builder.version
+ var snapshotModuleType string
+ if s.properties.Module_exports {
+ snapshotModuleType = "module_exports_snapshot"
+ } else {
+ snapshotModuleType = "sdk_snapshot"
+ }
+ snapshotModule := bpFile.newModule(snapshotModuleType)
+ snapshotModule.AddProperty("name", snapshotName)
+
+ // Make sure that the snapshot has the same visibility as the sdk.
+ visibility := android.EffectiveVisibilityRules(ctx, s)
+ if len(visibility) != 0 {
+ snapshotModule.AddProperty("visibility", visibility)
+ }
+
+ addHostDeviceSupportedProperties(s.ModuleBase.DeviceSupported(), s.ModuleBase.HostSupported(), snapshotModule)
+
+ var dynamicMemberPropertiesContainers []propertiesContainer
+ osTypeToMemberProperties := make(map[android.OsType]*sdk)
+ for _, sdkVariant := range sdkVariants {
+ properties := sdkVariant.dynamicMemberTypeListProperties
+ osTypeToMemberProperties[sdkVariant.Target().Os] = sdkVariant
+ dynamicMemberPropertiesContainers = append(dynamicMemberPropertiesContainers, &dynamicMemberPropertiesContainer{sdkVariant, properties})
+ }
+
+ // Extract the common lists of members into a separate struct.
+ commonDynamicMemberProperties := s.dynamicSdkMemberTypes.createMemberListProperties()
+ extractor := newCommonValueExtractor(commonDynamicMemberProperties)
+ extractCommonProperties(ctx, extractor, commonDynamicMemberProperties, dynamicMemberPropertiesContainers)
+
+ // Add properties common to all os types.
+ s.addMemberPropertiesToPropertySet(builder, snapshotModule, commonDynamicMemberProperties)
+
+ // Iterate over the os types in a fixed order.
+ targetPropertySet := snapshotModule.AddPropertySet("target")
+ for _, osType := range s.getPossibleOsTypes() {
+ if sdkVariant, ok := osTypeToMemberProperties[osType]; ok {
+ osPropertySet := targetPropertySet.AddPropertySet(sdkVariant.Target().Os.Name)
+
+ // Compile_multilib defaults to both and must always be set to both on the
+ // device and so only needs to be set when targeted at the host and is neither
+ // unspecified or both.
+ multilib := sdkVariant.multilibUsages
+ if (osType.Class == android.Host || osType.Class == android.HostCross) &&
+ multilib != multilibNone && multilib != multilibBoth {
+ osPropertySet.AddProperty("compile_multilib", multilib.String())
+ }
+
+ s.addMemberPropertiesToPropertySet(builder, osPropertySet, sdkVariant.dynamicMemberTypeListProperties)
+ }
+ }
+
+ // Prune any empty property sets.
+ snapshotModule.transform(pruneEmptySetTransformer{})
+
+ bpFile.AddModule(snapshotModule)
+
+ // generate Android.bp
+ bp = newGeneratedFile(ctx, "snapshot", "Android.bp")
+ generateBpContents(&bp.generatedContents, bpFile)
+
+ bp.build(pctx, ctx, nil)
+
+ filesToZip := builder.filesToZip
+
+ // zip them all
+ outputZipFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.zip").OutputPath
+ outputDesc := "Building snapshot for " + ctx.ModuleName()
+
+ // If there are no zips to merge then generate the output zip directly.
+ // Otherwise, generate an intermediate zip file into which other zips can be
+ // merged.
+ var zipFile android.OutputPath
+ var desc string
+ if len(builder.zipsToMerge) == 0 {
+ zipFile = outputZipFile
+ desc = outputDesc
+ } else {
+ zipFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.unmerged.zip").OutputPath
+ desc = "Building intermediate snapshot for " + ctx.ModuleName()
+ }
+
+ ctx.Build(pctx, android.BuildParams{
+ Description: desc,
+ Rule: zipFiles,
+ Inputs: filesToZip,
+ Output: zipFile,
+ Args: map[string]string{
+ "basedir": builder.snapshotDir.String(),
+ },
+ })
+
+ if len(builder.zipsToMerge) != 0 {
+ ctx.Build(pctx, android.BuildParams{
+ Description: outputDesc,
+ Rule: mergeZips,
+ Input: zipFile,
+ Inputs: builder.zipsToMerge,
+ Output: outputZipFile,
+ })
+ }
+
+ return outputZipFile
+}
+
+func extractCommonProperties(ctx android.ModuleContext, extractor *commonValueExtractor, commonProperties interface{}, inputPropertiesSlice interface{}) {
+ err := extractor.extractCommonProperties(commonProperties, inputPropertiesSlice)
+ if err != nil {
+ ctx.ModuleErrorf("error extracting common properties: %s", err)
+ }
+}
+
+func (s *sdk) addMemberPropertiesToPropertySet(builder *snapshotBuilder, propertySet android.BpPropertySet, dynamicMemberTypeListProperties interface{}) {
+ for _, memberListProperty := range s.memberListProperties() {
+ names := memberListProperty.getter(dynamicMemberTypeListProperties)
+ if len(names) > 0 {
+ propertySet.AddProperty(memberListProperty.propertyName(), builder.versionedSdkMemberNames(names, false))
+ }
+ }
+}
+
+type propertyTag struct {
+ name string
+}
+
+// A BpPropertyTag to add to a property that contains references to other sdk members.
+//
+// This will cause the references to be rewritten to a versioned reference in the version
+// specific instance of a snapshot module.
+var requiredSdkMemberReferencePropertyTag = propertyTag{"requiredSdkMemberReferencePropertyTag"}
+var optionalSdkMemberReferencePropertyTag = propertyTag{"optionalSdkMemberReferencePropertyTag"}
+
+// A BpPropertyTag that indicates the property should only be present in the versioned
+// module.
+//
+// This will cause the property to be removed from the unversioned instance of a
+// snapshot module.
+var sdkVersionedOnlyPropertyTag = propertyTag{"sdkVersionedOnlyPropertyTag"}
+
+type unversionedToVersionedTransformation struct {
+ identityTransformation
+ builder *snapshotBuilder
+}
+
+func (t unversionedToVersionedTransformation) transformModule(module *bpModule) *bpModule {
+ // Use a versioned name for the module but remember the original name for the
+ // snapshot.
+ name := module.getValue("name").(string)
+ module.setProperty("name", t.builder.versionedSdkMemberName(name, true))
+ module.insertAfter("name", "sdk_member_name", name)
+ return module
+}
+
+func (t unversionedToVersionedTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+ if tag == requiredSdkMemberReferencePropertyTag || tag == optionalSdkMemberReferencePropertyTag {
+ required := tag == requiredSdkMemberReferencePropertyTag
+ return t.builder.versionedSdkMemberNames(value.([]string), required), tag
+ } else {
+ return value, tag
+ }
+}
+
+type unversionedTransformation struct {
+ identityTransformation
+ builder *snapshotBuilder
+}
+
+func (t unversionedTransformation) transformModule(module *bpModule) *bpModule {
+ // If the module is an internal member then use a unique name for it.
+ name := module.getValue("name").(string)
+ module.setProperty("name", t.builder.unversionedSdkMemberName(name, true))
+
+ // Set prefer: false - this is not strictly required as that is the default.
+ module.insertAfter("name", "prefer", false)
+
+ return module
+}
+
+func (t unversionedTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
+ if tag == requiredSdkMemberReferencePropertyTag || tag == optionalSdkMemberReferencePropertyTag {
+ required := tag == requiredSdkMemberReferencePropertyTag
+ return t.builder.unversionedSdkMemberNames(value.([]string), required), tag
+ } else if tag == sdkVersionedOnlyPropertyTag {
+ // The property is not allowed in the unversioned module so remove it.
+ return nil, nil
+ } else {
+ return value, tag
+ }
+}
+
+type pruneEmptySetTransformer struct {
+ identityTransformation
+}
+
+var _ bpTransformer = (*pruneEmptySetTransformer)(nil)
+
+func (t pruneEmptySetTransformer) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+ if len(propertySet.properties) == 0 {
+ return nil, nil
+ } else {
+ return propertySet, tag
+ }
+}
+
+func generateBpContents(contents *generatedContents, bpFile *bpFile) {
+ contents.Printfln("// This is auto-generated. DO NOT EDIT.")
+ for _, bpModule := range bpFile.order {
+ contents.Printfln("")
+ contents.Printfln("%s {", bpModule.moduleType)
+ outputPropertySet(contents, bpModule.bpPropertySet)
+ contents.Printfln("}")
+ }
+}
+
+func outputPropertySet(contents *generatedContents, set *bpPropertySet) {
+ contents.Indent()
+
+ // Output the properties first, followed by the nested sets. This ensures a
+ // consistent output irrespective of whether property sets are created before
+ // or after the properties. This simplifies the creation of the module.
+ for _, name := range set.order {
+ value := set.getValue(name)
+
+ switch v := value.(type) {
+ case []string:
+ length := len(v)
+ if length > 1 {
+ contents.Printfln("%s: [", name)
+ contents.Indent()
+ for i := 0; i < length; i = i + 1 {
+ contents.Printfln("%q,", v[i])
+ }
+ contents.Dedent()
+ contents.Printfln("],")
+ } else if length == 0 {
+ contents.Printfln("%s: [],", name)
+ } else {
+ contents.Printfln("%s: [%q],", name, v[0])
+ }
+
+ case bool:
+ contents.Printfln("%s: %t,", name, v)
+
+ case *bpPropertySet:
+ // Do not write property sets in the properties phase.
+
+ default:
+ contents.Printfln("%s: %q,", name, value)
+ }
+ }
+
+ for _, name := range set.order {
+ value := set.getValue(name)
+
+ // Only write property sets in the sets phase.
+ switch v := value.(type) {
+ case *bpPropertySet:
+ contents.Printfln("%s: {", name)
+ outputPropertySet(contents, v)
+ contents.Printfln("},")
+ }
+ }
+
+ contents.Dedent()
+}
+
+func (s *sdk) GetAndroidBpContentsForTests() string {
+ contents := &generatedContents{}
+ generateBpContents(contents, s.builderForTests.bpFile)
+ return contents.content.String()
+}
+
+type snapshotBuilder struct {
+ ctx android.ModuleContext
+ sdk *sdk
+ version string
+ snapshotDir android.OutputPath
+ bpFile *bpFile
+
+ // Map from destination to source of each copy - used to eliminate duplicates and
+ // detect conflicts.
+ copies map[string]string
+
+ filesToZip android.Paths
+ zipsToMerge android.Paths
+
+ prebuiltModules map[string]*bpModule
+ prebuiltOrder []*bpModule
+
+ // The set of all members by name.
+ allMembersByName map[string]struct{}
+
+ // The set of exported members by name.
+ exportedMembersByName map[string]struct{}
+}
+
+func (s *snapshotBuilder) CopyToSnapshot(src android.Path, dest string) {
+ if existing, ok := s.copies[dest]; ok {
+ if existing != src.String() {
+ s.ctx.ModuleErrorf("conflicting copy, %s copied from both %s and %s", dest, existing, src)
+ return
+ }
+ } else {
+ path := s.snapshotDir.Join(s.ctx, dest)
+ s.ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: src,
+ Output: path,
+ })
+ s.filesToZip = append(s.filesToZip, path)
+
+ s.copies[dest] = src.String()
+ }
+}
+
+func (s *snapshotBuilder) UnzipToSnapshot(zipPath android.Path, destDir string) {
+ ctx := s.ctx
+
+ // Repackage the zip file so that the entries are in the destDir directory.
+ // This will allow the zip file to be merged into the snapshot.
+ tmpZipPath := android.PathForModuleOut(ctx, "tmp", destDir+".zip").OutputPath
+
+ ctx.Build(pctx, android.BuildParams{
+ Description: "Repackaging zip file " + destDir + " for snapshot " + ctx.ModuleName(),
+ Rule: repackageZip,
+ Input: zipPath,
+ Output: tmpZipPath,
+ Args: map[string]string{
+ "destdir": destDir,
+ },
+ })
+
+ // Add the repackaged zip file to the files to merge.
+ s.zipsToMerge = append(s.zipsToMerge, tmpZipPath)
+}
+
+func (s *snapshotBuilder) AddPrebuiltModule(member android.SdkMember, moduleType string) android.BpModule {
+ name := member.Name()
+ if s.prebuiltModules[name] != nil {
+ panic(fmt.Sprintf("Duplicate module detected, module %s has already been added", name))
+ }
+
+ m := s.bpFile.newModule(moduleType)
+ m.AddProperty("name", name)
+
+ variant := member.Variants()[0]
+
+ if s.isInternalMember(name) {
+ // An internal member is only referenced from the sdk snapshot which is in the
+ // same package so can be marked as private.
+ m.AddProperty("visibility", []string{"//visibility:private"})
+ } else {
+ // Extract visibility information from a member variant. All variants have the same
+ // visibility so it doesn't matter which one is used.
+ visibility := android.EffectiveVisibilityRules(s.ctx, variant)
+ if len(visibility) != 0 {
+ m.AddProperty("visibility", visibility)
+ }
+ }
+
+ deviceSupported := false
+ hostSupported := false
+
+ for _, variant := range member.Variants() {
+ osClass := variant.Target().Os.Class
+ if osClass == android.Host || osClass == android.HostCross {
+ hostSupported = true
+ } else if osClass == android.Device {
+ deviceSupported = true
+ }
+ }
+
+ addHostDeviceSupportedProperties(deviceSupported, hostSupported, m)
+
+ // Where available copy apex_available properties from the member.
+ if apexAware, ok := variant.(interface{ ApexAvailable() []string }); ok {
+ apexAvailable := apexAware.ApexAvailable()
+
+ // Add in any baseline apex available settings.
+ apexAvailable = append(apexAvailable, apex.BaselineApexAvailable(member.Name())...)
+
+ if len(apexAvailable) > 0 {
+ // Remove duplicates and sort.
+ apexAvailable = android.FirstUniqueStrings(apexAvailable)
+ sort.Strings(apexAvailable)
+
+ m.AddProperty("apex_available", apexAvailable)
+ }
+ }
+
+ // Disable installation in the versioned module of those modules that are ever installable.
+ if installable, ok := variant.(interface{ EverInstallable() bool }); ok {
+ if installable.EverInstallable() {
+ m.AddPropertyWithTag("installable", false, sdkVersionedOnlyPropertyTag)
+ }
+ }
+
+ s.prebuiltModules[name] = m
+ s.prebuiltOrder = append(s.prebuiltOrder, m)
+ return m
+}
+
+func addHostDeviceSupportedProperties(deviceSupported bool, hostSupported bool, bpModule *bpModule) {
+ if !deviceSupported {
+ bpModule.AddProperty("device_supported", false)
+ }
+ if hostSupported {
+ bpModule.AddProperty("host_supported", true)
+ }
+}
+
+func (s *snapshotBuilder) SdkMemberReferencePropertyTag(required bool) android.BpPropertyTag {
+ if required {
+ return requiredSdkMemberReferencePropertyTag
+ } else {
+ return optionalSdkMemberReferencePropertyTag
+ }
+}
+
+func (s *snapshotBuilder) OptionalSdkMemberReferencePropertyTag() android.BpPropertyTag {
+ return optionalSdkMemberReferencePropertyTag
+}
+
+// Get a versioned name appropriate for the SDK snapshot version being taken.
+func (s *snapshotBuilder) versionedSdkMemberName(unversionedName string, required bool) string {
+ if _, ok := s.allMembersByName[unversionedName]; !ok {
+ if required {
+ s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", unversionedName)
+ }
+ return unversionedName
+ }
+ return versionedSdkMemberName(s.ctx, unversionedName, s.version)
+}
+
+func (s *snapshotBuilder) versionedSdkMemberNames(members []string, required bool) []string {
+ var references []string = nil
+ for _, m := range members {
+ references = append(references, s.versionedSdkMemberName(m, required))
+ }
+ return references
+}
+
+// Get an internal name unique to the sdk.
+func (s *snapshotBuilder) unversionedSdkMemberName(unversionedName string, required bool) string {
+ if _, ok := s.allMembersByName[unversionedName]; !ok {
+ if required {
+ s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", unversionedName)
+ }
+ return unversionedName
+ }
+
+ if s.isInternalMember(unversionedName) {
+ return s.ctx.ModuleName() + "_" + unversionedName
+ } else {
+ return unversionedName
+ }
+}
+
+func (s *snapshotBuilder) unversionedSdkMemberNames(members []string, required bool) []string {
+ var references []string = nil
+ for _, m := range members {
+ references = append(references, s.unversionedSdkMemberName(m, required))
+ }
+ return references
+}
+
+func (s *snapshotBuilder) isInternalMember(memberName string) bool {
+ _, ok := s.exportedMembersByName[memberName]
+ return !ok
+}
+
+type sdkMemberRef struct {
+ memberType android.SdkMemberType
+ variant android.SdkAware
+}
+
+var _ android.SdkMember = (*sdkMember)(nil)
+
+type sdkMember struct {
+ memberType android.SdkMemberType
+ name string
+ variants []android.SdkAware
+}
+
+func (m *sdkMember) Name() string {
+ return m.name
+}
+
+func (m *sdkMember) Variants() []android.SdkAware {
+ return m.variants
+}
+
+// Track usages of multilib variants.
+type multilibUsage int
+
+const (
+ multilibNone multilibUsage = 0
+ multilib32 multilibUsage = 1
+ multilib64 multilibUsage = 2
+ multilibBoth = multilib32 | multilib64
+)
+
+// Add the multilib that is used in the arch type.
+func (m multilibUsage) addArchType(archType android.ArchType) multilibUsage {
+ multilib := archType.Multilib
+ switch multilib {
+ case "":
+ return m
+ case "lib32":
+ return m | multilib32
+ case "lib64":
+ return m | multilib64
+ default:
+ panic(fmt.Errorf("Unknown Multilib field in ArchType, expected 'lib32' or 'lib64', found %q", multilib))
+ }
+}
+
+func (m multilibUsage) String() string {
+ switch m {
+ case multilibNone:
+ return ""
+ case multilib32:
+ return "32"
+ case multilib64:
+ return "64"
+ case multilibBoth:
+ return "both"
+ default:
+ panic(fmt.Errorf("Unknown multilib value, found %b, expected one of %b, %b, %b or %b",
+ m, multilibNone, multilib32, multilib64, multilibBoth))
+ }
+}
+
+type baseInfo struct {
+ Properties android.SdkMemberProperties
+}
+
+func (b *baseInfo) optimizableProperties() interface{} {
+ return b.Properties
+}
+
+type osTypeSpecificInfo struct {
+ baseInfo
+
+ osType android.OsType
+
+ // The list of arch type specific info for this os type.
+ //
+ // Nil if there is one variant whose arch type is common
+ archInfos []*archTypeSpecificInfo
+}
+
+var _ propertiesContainer = (*osTypeSpecificInfo)(nil)
+
+type variantPropertiesFactoryFunc func() android.SdkMemberProperties
+
+// Create a new osTypeSpecificInfo for the specified os type and its properties
+// structures populated with information from the variants.
+func newOsTypeSpecificInfo(ctx android.SdkMemberContext, osType android.OsType, variantPropertiesFactory variantPropertiesFactoryFunc, osTypeVariants []android.Module) *osTypeSpecificInfo {
+ osInfo := &osTypeSpecificInfo{
+ osType: osType,
+ }
+
+ osSpecificVariantPropertiesFactory := func() android.SdkMemberProperties {
+ properties := variantPropertiesFactory()
+ properties.Base().Os = osType
+ return properties
+ }
+
+ // Create a structure into which properties common across the architectures in
+ // this os type will be stored.
+ osInfo.Properties = osSpecificVariantPropertiesFactory()
+
+ // Group the variants by arch type.
+ var variantsByArchName = make(map[string][]android.Module)
+ var archTypes []android.ArchType
+ for _, variant := range osTypeVariants {
+ archType := variant.Target().Arch.ArchType
+ archTypeName := archType.Name
+ if _, ok := variantsByArchName[archTypeName]; !ok {
+ archTypes = append(archTypes, archType)
+ }
+
+ variantsByArchName[archTypeName] = append(variantsByArchName[archTypeName], variant)
+ }
+
+ if commonVariants, ok := variantsByArchName["common"]; ok {
+ if len(osTypeVariants) != 1 {
+ panic("Expected to only have 1 variant when arch type is common but found " + string(len(osTypeVariants)))
+ }
+
+ // A common arch type only has one variant and its properties should be treated
+ // as common to the os type.
+ osInfo.Properties.PopulateFromVariant(ctx, commonVariants[0])
+ } else {
+ // Create an arch specific info for each supported architecture type.
+ for _, archType := range archTypes {
+ archTypeName := archType.Name
+
+ archVariants := variantsByArchName[archTypeName]
+ archInfo := newArchSpecificInfo(ctx, archType, osSpecificVariantPropertiesFactory, archVariants)
+
+ osInfo.archInfos = append(osInfo.archInfos, archInfo)
+ }
+ }
+
+ return osInfo
+}
+
+// Optimize the properties by extracting common properties from arch type specific
+// properties into os type specific properties.
+func (osInfo *osTypeSpecificInfo) optimizeProperties(ctx *memberContext, commonValueExtractor *commonValueExtractor) {
+ // Nothing to do if there is only a single common architecture.
+ if len(osInfo.archInfos) == 0 {
+ return
+ }
+
+ multilib := multilibNone
+ for _, archInfo := range osInfo.archInfos {
+ multilib = multilib.addArchType(archInfo.archType)
+
+ // Optimize the arch properties first.
+ archInfo.optimizeProperties(ctx, commonValueExtractor)
+ }
+
+ extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, osInfo.Properties, osInfo.archInfos)
+
+ // Choose setting for compile_multilib that is appropriate for the arch variants supplied.
+ osInfo.Properties.Base().Compile_multilib = multilib.String()
+}
+
+// Add the properties for an os to a property set.
+//
+// Maps the properties related to the os variants through to an appropriate
+// module structure that will produce equivalent set of variants when it is
+// processed in a build.
+func (osInfo *osTypeSpecificInfo) addToPropertySet(ctx *memberContext, bpModule android.BpModule, targetPropertySet android.BpPropertySet) {
+
+ var osPropertySet android.BpPropertySet
+ var archPropertySet android.BpPropertySet
+ var archOsPrefix string
+ if osInfo.Properties.Base().Os_count == 1 {
+ // There is only one os type present in the variants so don't bother
+ // with adding target specific properties.
+
+ // Create a structure that looks like:
+ // module_type {
+ // name: "...",
+ // ...
+ // <common properties>
+ // ...
+ // <single os type specific properties>
+ //
+ // arch: {
+ // <arch specific sections>
+ // }
+ //
+ osPropertySet = bpModule
+ archPropertySet = osPropertySet.AddPropertySet("arch")
+
+ // Arch specific properties need to be added to an arch specific section
+ // within arch.
+ archOsPrefix = ""
+ } else {
+ // Create a structure that looks like:
+ // module_type {
+ // name: "...",
+ // ...
+ // <common properties>
+ // ...
+ // target: {
+ // <arch independent os specific sections, e.g. android>
+ // ...
+ // <arch and os specific sections, e.g. android_x86>
+ // }
+ //
+ osType := osInfo.osType
+ osPropertySet = targetPropertySet.AddPropertySet(osType.Name)
+ archPropertySet = targetPropertySet
+
+ // Arch specific properties need to be added to an os and arch specific
+ // section prefixed with <os>_.
+ archOsPrefix = osType.Name + "_"
+ }
+
+ // Add the os specific but arch independent properties to the module.
+ osInfo.Properties.AddToPropertySet(ctx, osPropertySet)
+
+ // Add arch (and possibly os) specific sections for each set of arch (and possibly
+ // os) specific properties.
+ //
+ // The archInfos list will be empty if the os contains variants for the common
+ // architecture.
+ for _, archInfo := range osInfo.archInfos {
+ archInfo.addToPropertySet(ctx, archPropertySet, archOsPrefix)
+ }
+}
+
+func (osInfo *osTypeSpecificInfo) isHostVariant() bool {
+ osClass := osInfo.osType.Class
+ return osClass == android.Host || osClass == android.HostCross
+}
+
+var _ isHostVariant = (*osTypeSpecificInfo)(nil)
+
+func (osInfo *osTypeSpecificInfo) String() string {
+ return fmt.Sprintf("OsType{%s}", osInfo.osType)
+}
+
+type archTypeSpecificInfo struct {
+ baseInfo
+
+ archType android.ArchType
+
+ linkInfos []*linkTypeSpecificInfo
+}
+
+var _ propertiesContainer = (*archTypeSpecificInfo)(nil)
+
+// Create a new archTypeSpecificInfo for the specified arch type and its properties
+// structures populated with information from the variants.
+func newArchSpecificInfo(ctx android.SdkMemberContext, archType android.ArchType, variantPropertiesFactory variantPropertiesFactoryFunc, archVariants []android.Module) *archTypeSpecificInfo {
+
+ // Create an arch specific info into which the variant properties can be copied.
+ archInfo := &archTypeSpecificInfo{archType: archType}
+
+ // Create the properties into which the arch type specific properties will be
+ // added.
+ archInfo.Properties = variantPropertiesFactory()
+
+ if len(archVariants) == 1 {
+ archInfo.Properties.PopulateFromVariant(ctx, archVariants[0])
+ } else {
+ // There is more than one variant for this arch type which must be differentiated
+ // by link type.
+ for _, linkVariant := range archVariants {
+ linkType := getLinkType(linkVariant)
+ if linkType == "" {
+ panic(fmt.Errorf("expected one arch specific variant as it is not identified by link type but found %d", len(archVariants)))
+ } else {
+ linkInfo := newLinkSpecificInfo(ctx, linkType, variantPropertiesFactory, linkVariant)
+
+ archInfo.linkInfos = append(archInfo.linkInfos, linkInfo)
+ }
+ }
+ }
+
+ return archInfo
+}
+
+func (archInfo *archTypeSpecificInfo) optimizableProperties() interface{} {
+ return archInfo.Properties
+}
+
+// Get the link type of the variant
+//
+// If the variant is not differentiated by link type then it returns "",
+// otherwise it returns one of "static" or "shared".
+func getLinkType(variant android.Module) string {
+ linkType := ""
+ if linkable, ok := variant.(cc.LinkableInterface); ok {
+ if linkable.Shared() && linkable.Static() {
+ panic(fmt.Errorf("expected variant %q to be either static or shared but was both", variant.String()))
+ } else if linkable.Shared() {
+ linkType = "shared"
+ } else if linkable.Static() {
+ linkType = "static"
+ } else {
+ panic(fmt.Errorf("expected variant %q to be either static or shared but was neither", variant.String()))
+ }
+ }
+ return linkType
+}
+
+// Optimize the properties by extracting common properties from link type specific
+// properties into arch type specific properties.
+func (archInfo *archTypeSpecificInfo) optimizeProperties(ctx *memberContext, commonValueExtractor *commonValueExtractor) {
+ if len(archInfo.linkInfos) == 0 {
+ return
+ }
+
+ extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, archInfo.Properties, archInfo.linkInfos)
+}
+
+// Add the properties for an arch type to a property set.
+func (archInfo *archTypeSpecificInfo) addToPropertySet(ctx *memberContext, archPropertySet android.BpPropertySet, archOsPrefix string) {
+ archTypeName := archInfo.archType.Name
+ archTypePropertySet := archPropertySet.AddPropertySet(archOsPrefix + archTypeName)
+ archInfo.Properties.AddToPropertySet(ctx, archTypePropertySet)
+
+ for _, linkInfo := range archInfo.linkInfos {
+ linkPropertySet := archTypePropertySet.AddPropertySet(linkInfo.linkType)
+ linkInfo.Properties.AddToPropertySet(ctx, linkPropertySet)
+ }
+}
+
+func (archInfo *archTypeSpecificInfo) String() string {
+ return fmt.Sprintf("ArchType{%s}", archInfo.archType)
+}
+
+type linkTypeSpecificInfo struct {
+ baseInfo
+
+ linkType string
+}
+
+var _ propertiesContainer = (*linkTypeSpecificInfo)(nil)
+
+// Create a new linkTypeSpecificInfo for the specified link type and its properties
+// structures populated with information from the variant.
+func newLinkSpecificInfo(ctx android.SdkMemberContext, linkType string, variantPropertiesFactory variantPropertiesFactoryFunc, linkVariant android.Module) *linkTypeSpecificInfo {
+ linkInfo := &linkTypeSpecificInfo{
+ baseInfo: baseInfo{
+ // Create the properties into which the link type specific properties will be
+ // added.
+ Properties: variantPropertiesFactory(),
+ },
+ linkType: linkType,
+ }
+ linkInfo.Properties.PopulateFromVariant(ctx, linkVariant)
+ return linkInfo
+}
+
+func (l *linkTypeSpecificInfo) String() string {
+ return fmt.Sprintf("LinkType{%s}", l.linkType)
+}
+
+type memberContext struct {
+ sdkMemberContext android.ModuleContext
+ builder *snapshotBuilder
+ memberType android.SdkMemberType
+ name string
+}
+
+func (m *memberContext) SdkModuleContext() android.ModuleContext {
+ return m.sdkMemberContext
+}
+
+func (m *memberContext) SnapshotBuilder() android.SnapshotBuilder {
+ return m.builder
+}
+
+func (m *memberContext) MemberType() android.SdkMemberType {
+ return m.memberType
+}
+
+func (m *memberContext) Name() string {
+ return m.name
+}
+
+func (s *sdk) createMemberSnapshot(ctx *memberContext, member *sdkMember, bpModule android.BpModule) {
+
+ memberType := member.memberType
+
+ // Group the variants by os type.
+ variantsByOsType := make(map[android.OsType][]android.Module)
+ variants := member.Variants()
+ for _, variant := range variants {
+ osType := variant.Target().Os
+ variantsByOsType[osType] = append(variantsByOsType[osType], variant)
+ }
+
+ osCount := len(variantsByOsType)
+ variantPropertiesFactory := func() android.SdkMemberProperties {
+ properties := memberType.CreateVariantPropertiesStruct()
+ base := properties.Base()
+ base.Os_count = osCount
+ return properties
+ }
+
+ osTypeToInfo := make(map[android.OsType]*osTypeSpecificInfo)
+
+ // The set of properties that are common across all architectures and os types.
+ commonProperties := variantPropertiesFactory()
+ commonProperties.Base().Os = android.CommonOS
+
+ // Create common value extractor that can be used to optimize the properties.
+ commonValueExtractor := newCommonValueExtractor(commonProperties)
+
+ // The list of property structures which are os type specific but common across
+ // architectures within that os type.
+ var osSpecificPropertiesContainers []*osTypeSpecificInfo
+
+ for osType, osTypeVariants := range variantsByOsType {
+ osInfo := newOsTypeSpecificInfo(ctx, osType, variantPropertiesFactory, osTypeVariants)
+ osTypeToInfo[osType] = osInfo
+ // Add the os specific properties to a list of os type specific yet architecture
+ // independent properties structs.
+ osSpecificPropertiesContainers = append(osSpecificPropertiesContainers, osInfo)
+
+ // Optimize the properties across all the variants for a specific os type.
+ osInfo.optimizeProperties(ctx, commonValueExtractor)
+ }
+
+ // Extract properties which are common across all architectures and os types.
+ extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, commonProperties, osSpecificPropertiesContainers)
+
+ // Add the common properties to the module.
+ commonProperties.AddToPropertySet(ctx, bpModule)
+
+ // Create a target property set into which target specific properties can be
+ // added.
+ targetPropertySet := bpModule.AddPropertySet("target")
+
+ // Iterate over the os types in a fixed order.
+ for _, osType := range s.getPossibleOsTypes() {
+ osInfo := osTypeToInfo[osType]
+ if osInfo == nil {
+ continue
+ }
+
+ osInfo.addToPropertySet(ctx, bpModule, targetPropertySet)
+ }
+}
+
+// Compute the list of possible os types that this sdk could support.
+func (s *sdk) getPossibleOsTypes() []android.OsType {
+ var osTypes []android.OsType
+ for _, osType := range android.OsTypeList {
+ if s.DeviceSupported() {
+ if osType.Class == android.Device && osType != android.Fuchsia {
+ osTypes = append(osTypes, osType)
+ }
+ }
+ if s.HostSupported() {
+ if osType.Class == android.Host || osType.Class == android.HostCross {
+ osTypes = append(osTypes, osType)
+ }
+ }
+ }
+ sort.SliceStable(osTypes, func(i, j int) bool { return osTypes[i].Name < osTypes[j].Name })
+ return osTypes
+}
+
+// Given a set of properties (struct value), return the value of the field within that
+// struct (or one of its embedded structs).
+type fieldAccessorFunc func(structValue reflect.Value) reflect.Value
+
+// Checks the metadata to determine whether the property should be ignored for the
+// purposes of common value extraction or not.
+type extractorMetadataPredicate func(metadata propertiesContainer) bool
+
+// Indicates whether optimizable properties are provided by a host variant or
+// not.
+type isHostVariant interface {
+ isHostVariant() bool
+}
+
+// A property that can be optimized by the commonValueExtractor.
+type extractorProperty struct {
+ // The name of the field for this property.
+ name string
+
+ // Filter that can use metadata associated with the properties being optimized
+ // to determine whether the field should be ignored during common value
+ // optimization.
+ filter extractorMetadataPredicate
+
+ // Retrieves the value on which common value optimization will be performed.
+ getter fieldAccessorFunc
+
+ // The empty value for the field.
+ emptyValue reflect.Value
+
+ // True if the property can support arch variants false otherwise.
+ archVariant bool
+}
+
+func (p extractorProperty) String() string {
+ return p.name
+}
+
+// Supports extracting common values from a number of instances of a properties
+// structure into a separate common set of properties.
+type commonValueExtractor struct {
+ // The properties that the extractor can optimize.
+ properties []extractorProperty
+}
+
+// Create a new common value extractor for the structure type for the supplied
+// properties struct.
+//
+// The returned extractor can be used on any properties structure of the same type
+// as the supplied set of properties.
+func newCommonValueExtractor(propertiesStruct interface{}) *commonValueExtractor {
+ structType := getStructValue(reflect.ValueOf(propertiesStruct)).Type()
+ extractor := &commonValueExtractor{}
+ extractor.gatherFields(structType, nil)
+ return extractor
+}
+
+// Gather the fields from the supplied structure type from which common values will
+// be extracted.
+//
+// This is recursive function. If it encounters an embedded field (no field name)
+// that is a struct then it will recurse into that struct passing in the accessor
+// for the field. That will then be used in the accessors for the fields in the
+// embedded struct.
+func (e *commonValueExtractor) gatherFields(structType reflect.Type, containingStructAccessor fieldAccessorFunc) {
+ for f := 0; f < structType.NumField(); f++ {
+ field := structType.Field(f)
+ if field.PkgPath != "" {
+ // Ignore unexported fields.
+ continue
+ }
+
+ // Ignore fields whose value should be kept.
+ if proptools.HasTag(field, "sdk", "keep") {
+ continue
+ }
+
+ var filter extractorMetadataPredicate
+
+ // Add a filter
+ if proptools.HasTag(field, "sdk", "ignored-on-host") {
+ filter = func(metadata propertiesContainer) bool {
+ if m, ok := metadata.(isHostVariant); ok {
+ if m.isHostVariant() {
+ return false
+ }
+ }
+ return true
+ }
+ }
+
+ // Save a copy of the field index for use in the function.
+ fieldIndex := f
+
+ name := field.Name
+
+ fieldGetter := func(value reflect.Value) reflect.Value {
+ if containingStructAccessor != nil {
+ // This is an embedded structure so first access the field for the embedded
+ // structure.
+ value = containingStructAccessor(value)
+ }
+
+ // Skip through interface and pointer values to find the structure.
+ value = getStructValue(value)
+
+ defer func() {
+ if r := recover(); r != nil {
+ panic(fmt.Errorf("%s for fieldIndex %d of field %s of value %#v", r, fieldIndex, name, value.Interface()))
+ }
+ }()
+
+ // Return the field.
+ return value.Field(fieldIndex)
+ }
+
+ if field.Type.Kind() == reflect.Struct && field.Anonymous {
+ // Gather fields from the embedded structure.
+ e.gatherFields(field.Type, fieldGetter)
+ } else {
+ property := extractorProperty{
+ name,
+ filter,
+ fieldGetter,
+ reflect.Zero(field.Type),
+ proptools.HasTag(field, "android", "arch_variant"),
+ }
+ e.properties = append(e.properties, property)
+ }
+ }
+}
+
+func getStructValue(value reflect.Value) reflect.Value {
+foundStruct:
+ for {
+ kind := value.Kind()
+ switch kind {
+ case reflect.Interface, reflect.Ptr:
+ value = value.Elem()
+ case reflect.Struct:
+ break foundStruct
+ default:
+ panic(fmt.Errorf("expecting struct, interface or pointer, found %v of kind %s", value, kind))
+ }
+ }
+ return value
+}
+
+// A container of properties to be optimized.
+//
+// Allows additional information to be associated with the properties, e.g. for
+// filtering.
+type propertiesContainer interface {
+ fmt.Stringer
+
+ // Get the properties that need optimizing.
+ optimizableProperties() interface{}
+}
+
+// A wrapper for dynamic member properties to allow them to be optimized.
+type dynamicMemberPropertiesContainer struct {
+ sdkVariant *sdk
+ dynamicMemberProperties interface{}
+}
+
+func (c dynamicMemberPropertiesContainer) optimizableProperties() interface{} {
+ return c.dynamicMemberProperties
+}
+
+func (c dynamicMemberPropertiesContainer) String() string {
+ return c.sdkVariant.String()
+}
+
+// Extract common properties from a slice of property structures of the same type.
+//
+// All the property structures must be of the same type.
+// commonProperties - must be a pointer to the structure into which common properties will be added.
+// inputPropertiesSlice - must be a slice of propertiesContainer interfaces.
+//
+// Iterates over each exported field (capitalized name) and checks to see whether they
+// have the same value (using DeepEquals) across all the input properties. If it does not then no
+// change is made. Otherwise, the common value is stored in the field in the commonProperties
+// and the field in each of the input properties structure is set to its default value.
+func (e *commonValueExtractor) extractCommonProperties(commonProperties interface{}, inputPropertiesSlice interface{}) error {
+ commonPropertiesValue := reflect.ValueOf(commonProperties)
+ commonStructValue := commonPropertiesValue.Elem()
+
+ sliceValue := reflect.ValueOf(inputPropertiesSlice)
+
+ for _, property := range e.properties {
+ fieldGetter := property.getter
+ filter := property.filter
+ if filter == nil {
+ filter = func(metadata propertiesContainer) bool {
+ return true
+ }
+ }
+
+ // Check to see if all the structures have the same value for the field. The commonValue
+ // is nil on entry to the loop and if it is nil on exit then there is no common value or
+ // all the values have been filtered out, otherwise it points to the common value.
+ var commonValue *reflect.Value
+
+ // Assume that all the values will be the same.
+ //
+ // While similar to this is not quite the same as commonValue == nil. If all the values
+ // have been filtered out then this will be false but commonValue == nil will be true.
+ valuesDiffer := false
+
+ for i := 0; i < sliceValue.Len(); i++ {
+ container := sliceValue.Index(i).Interface().(propertiesContainer)
+ itemValue := reflect.ValueOf(container.optimizableProperties())
+ fieldValue := fieldGetter(itemValue)
+
+ if !filter(container) {
+ expectedValue := property.emptyValue.Interface()
+ actualValue := fieldValue.Interface()
+ if !reflect.DeepEqual(expectedValue, actualValue) {
+ return fmt.Errorf("field %q is supposed to be ignored for %q but is set to %#v instead of %#v", property, container, actualValue, expectedValue)
+ }
+ continue
+ }
+
+ if commonValue == nil {
+ // Use the first value as the commonProperties value.
+ commonValue = &fieldValue
+ } else {
+ // If the value does not match the current common value then there is
+ // no value in common so break out.
+ if !reflect.DeepEqual(fieldValue.Interface(), commonValue.Interface()) {
+ commonValue = nil
+ valuesDiffer = true
+ break
+ }
+ }
+ }
+
+ // If the fields all have common value then store it in the common struct field
+ // and set the input struct's field to the empty value.
+ if commonValue != nil {
+ emptyValue := property.emptyValue
+ fieldGetter(commonStructValue).Set(*commonValue)
+ for i := 0; i < sliceValue.Len(); i++ {
+ container := sliceValue.Index(i).Interface().(propertiesContainer)
+ itemValue := reflect.ValueOf(container.optimizableProperties())
+ fieldValue := fieldGetter(itemValue)
+ fieldValue.Set(emptyValue)
+ }
+ }
+
+ if valuesDiffer && !property.archVariant {
+ // The values differ but the property does not support arch variants so it
+ // is an error.
+ var details strings.Builder
+ for i := 0; i < sliceValue.Len(); i++ {
+ container := sliceValue.Index(i).Interface().(propertiesContainer)
+ itemValue := reflect.ValueOf(container.optimizableProperties())
+ fieldValue := fieldGetter(itemValue)
+
+ _, _ = fmt.Fprintf(&details, "\n %q has value %q", container.String(), fieldValue.Interface())
+ }
+
+ return fmt.Errorf("field %q is not tagged as \"arch_variant\" but has arch specific properties:%s", property.String(), details.String())
+ }
+ }
+
+ return nil
+}