Merge "Improve android_app_import.dpi_variants handling."
diff --git a/android/androidmk.go b/android/androidmk.go
index 7d0aa3b..9bc2692 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -216,7 +216,10 @@
}
a.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(amod.commonProperties.Device_specific))
a.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(amod.commonProperties.Product_specific))
- a.SetBoolIfTrue("LOCAL_PRODUCT_SERVICES_MODULE", Bool(amod.commonProperties.Product_services_specific))
+ // TODO(b/135957588) product_services_specific is matched to LOCAL_PRODUCT_MODULE
+ // as a workaround. Remove this after clearing all Android.bp
+ a.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(amod.commonProperties.Product_services_specific))
+ a.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(amod.commonProperties.System_ext_specific))
if amod.commonProperties.Owner != nil {
a.SetString("LOCAL_MODULE_OWNER", *amod.commonProperties.Owner)
}
diff --git a/android/config.go b/android/config.go
index e4012fa..c0ed50f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -901,11 +901,11 @@
return "product"
}
-func (c *deviceConfig) ProductServicesPath() string {
- if c.config.productVariables.ProductServicesPath != nil {
- return *c.config.productVariables.ProductServicesPath
+func (c *deviceConfig) SystemExtPath() string {
+ if c.config.productVariables.SystemExtPath != nil {
+ return *c.config.productVariables.SystemExtPath
}
- return "product_services"
+ return "system_ext"
}
func (c *deviceConfig) BtConfigIncludeDir() string {
diff --git a/android/defaults.go b/android/defaults.go
index 844b4d4..ae2c820 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -63,9 +63,28 @@
type DefaultsModuleBase struct {
DefaultableModuleBase
- defaultProperties []interface{}
}
+// The common pattern for defaults modules is to register separate instances of
+// the xxxProperties structs in the AddProperties calls, rather than reusing the
+// ones inherited from Module.
+//
+// The effect is that e.g. myDefaultsModuleInstance.base().xxxProperties won't
+// contain the values that have been set for the defaults module. Rather, to
+// retrieve the values it is necessary to iterate over properties(). E.g. to get
+// the commonProperties instance that have the real values:
+//
+// d := myModule.(Defaults)
+// for _, props := range d.properties() {
+// if cp, ok := props.(*commonProperties); ok {
+// ... access property values in cp ...
+// }
+// }
+//
+// The rationale is that the properties on a defaults module apply to the
+// defaultable modules using it, not to the defaults module itself. E.g. setting
+// the "enabled" property false makes inheriting modules disabled by default,
+// rather than disabling the defaults module itself.
type Defaults interface {
Defaultable
isDefaults() bool
diff --git a/android/module.go b/android/module.go
index 2d4c1b5..adb9454 100644
--- a/android/module.go
+++ b/android/module.go
@@ -126,7 +126,7 @@
DeviceSpecific() bool
SocSpecific() bool
ProductSpecific() bool
- ProductServicesSpecific() bool
+ SystemExtSpecific() bool
AConfig() Config
DeviceConfig() DeviceConfig
}
@@ -350,11 +350,15 @@
// /system/product if product partition does not exist).
Product_specific *bool
- // whether this module provides services owned by the OS provider to the core platform. When set
- // to true, it is installed into /product_services (or /system/product_services if
- // product_services partition does not exist).
+ // TODO(b/135957588) Product_services_specific will be removed once we clear all Android.bp
+ // files that have 'product_services_specific: true'. This will be converted to
+ // Product_speicific as a workaround.
Product_services_specific *bool
+ // whether this module extends system. When set to true, it is installed into /system_ext
+ // (or /system/system_ext if system_ext partition does not exist).
+ System_ext_specific *bool
+
// Whether this module is installed to recovery partition
Recovery *bool
@@ -469,7 +473,7 @@
deviceSpecificModule
socSpecificModule
productSpecificModule
- productServicesSpecificModule
+ systemExtSpecificModule
)
func (k moduleKind) String() string {
@@ -482,8 +486,8 @@
return "soc-specific"
case productSpecificModule:
return "product-specific"
- case productServicesSpecificModule:
- return "productservices-specific"
+ case systemExtSpecificModule:
+ return "systemext-specific"
default:
panic(fmt.Errorf("unknown module kind %d", k))
}
@@ -738,7 +742,7 @@
}
func (m *ModuleBase) Platform() bool {
- return !m.DeviceSpecific() && !m.SocSpecific() && !m.ProductSpecific() && !m.ProductServicesSpecific()
+ return !m.DeviceSpecific() && !m.SocSpecific() && !m.ProductSpecific() && !m.SystemExtSpecific()
}
func (m *ModuleBase) DeviceSpecific() bool {
@@ -753,8 +757,8 @@
return Bool(m.commonProperties.Product_specific)
}
-func (m *ModuleBase) ProductServicesSpecific() bool {
- return Bool(m.commonProperties.Product_services_specific)
+func (m *ModuleBase) SystemExtSpecific() bool {
+ return Bool(m.commonProperties.System_ext_specific)
}
func (m *ModuleBase) Enabled() bool {
@@ -875,7 +879,7 @@
var socSpecific = Bool(m.commonProperties.Vendor) || Bool(m.commonProperties.Proprietary) || Bool(m.commonProperties.Soc_specific)
var deviceSpecific = Bool(m.commonProperties.Device_specific)
var productSpecific = Bool(m.commonProperties.Product_specific)
- var productServicesSpecific = Bool(m.commonProperties.Product_services_specific)
+ var systemExtSpecific = Bool(m.commonProperties.System_ext_specific)
msg := "conflicting value set here"
if socSpecific && deviceSpecific {
@@ -891,16 +895,16 @@
}
}
- if productSpecific && productServicesSpecific {
- ctx.PropertyErrorf("product_specific", "a module cannot be specific to product and product_services at the same time.")
- ctx.PropertyErrorf("product_services_specific", msg)
+ if productSpecific && systemExtSpecific {
+ ctx.PropertyErrorf("product_specific", "a module cannot be specific to product and system_ext at the same time.")
+ ctx.PropertyErrorf("system_ext_specific", msg)
}
- if (socSpecific || deviceSpecific) && (productSpecific || productServicesSpecific) {
+ if (socSpecific || deviceSpecific) && (productSpecific || systemExtSpecific) {
if productSpecific {
ctx.PropertyErrorf("product_specific", "a module cannot be specific to SoC or device and product at the same time.")
} else {
- ctx.PropertyErrorf("product_services_specific", "a module cannot be specific to SoC or device and product_services at the same time.")
+ ctx.PropertyErrorf("system_ext_specific", "a module cannot be specific to SoC or device and system_ext at the same time.")
}
if deviceSpecific {
ctx.PropertyErrorf("device_specific", msg)
@@ -919,8 +923,8 @@
if productSpecific {
return productSpecificModule
- } else if productServicesSpecific {
- return productServicesSpecificModule
+ } else if systemExtSpecific {
+ return systemExtSpecificModule
} else if deviceSpecific {
return deviceSpecificModule
} else if socSpecific {
@@ -1433,18 +1437,18 @@
return b.kind == productSpecificModule
}
-func (b *baseModuleContext) ProductServicesSpecific() bool {
- return b.kind == productServicesSpecificModule
+func (b *baseModuleContext) SystemExtSpecific() bool {
+ return b.kind == systemExtSpecificModule
}
// Makes this module a platform module, i.e. not specific to soc, device,
-// product, or product_services.
+// product, or system_ext.
func (m *ModuleBase) MakeAsPlatform() {
m.commonProperties.Vendor = boolPtr(false)
m.commonProperties.Proprietary = boolPtr(false)
m.commonProperties.Soc_specific = boolPtr(false)
m.commonProperties.Product_specific = boolPtr(false)
- m.commonProperties.Product_services_specific = boolPtr(false)
+ m.commonProperties.System_ext_specific = boolPtr(false)
}
func (m *ModuleBase) EnableNativeBridgeSupportByDefault() {
diff --git a/android/notices.go b/android/notices.go
index dbb88fc..8503593 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -27,6 +27,13 @@
pctx.HostBinToolVariable("minigzip", "minigzip")
}
+type NoticeOutputs struct {
+ Merged OptionalPath
+ TxtOutput OptionalPath
+ HtmlOutput OptionalPath
+ HtmlGzOutput OptionalPath
+}
+
var (
mergeNoticesRule = pctx.AndroidStaticRule("mergeNoticesRule", blueprint.RuleParams{
Command: `${merge_notices} --output $out $in`,
@@ -35,13 +42,13 @@
})
generateNoticeRule = pctx.AndroidStaticRule("generateNoticeRule", blueprint.RuleParams{
- Command: `rm -rf $tmpDir $$(dirname $out) && ` +
- `mkdir -p $tmpDir $$(dirname $out) && ` +
- `${generate_notice} --text-output $tmpDir/NOTICE.txt --html-output $tmpDir/NOTICE.html -t "$title" -s $inputDir && ` +
- `${minigzip} -c $tmpDir/NOTICE.html > $out`,
+ Command: `rm -rf $$(dirname $txtOut) $$(dirname htmlOut) $$(dirname $out) && ` +
+ `mkdir -p $$(dirname $txtOut) $$(dirname htmlOut) $$(dirname $out) && ` +
+ `${generate_notice} --text-output $txtOut --html-output $htmlOut -t "$title" -s $inputDir && ` +
+ `${minigzip} -c $htmlOut > $out`,
CommandDeps: []string{"${generate_notice}", "${minigzip}"},
Description: "produce notice file $out",
- }, "tmpDir", "title", "inputDir")
+ }, "txtOut", "htmlOut", "title", "inputDir")
)
func MergeNotices(ctx ModuleContext, mergedNotice WritablePath, noticePaths []Path) {
@@ -54,7 +61,7 @@
}
func BuildNoticeOutput(ctx ModuleContext, installPath OutputPath, installFilename string,
- noticePaths []Path) ModuleOutPath {
+ noticePaths []Path) NoticeOutputs {
// Merge all NOTICE files into one.
// TODO(jungjw): We should just produce a well-formatted NOTICE.html file in a single pass.
//
@@ -68,20 +75,28 @@
MergeNotices(ctx, mergedNotice, noticePaths)
// Transform the merged NOTICE file into a gzipped HTML file.
- noticeOutput := PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
- tmpDir := PathForModuleOut(ctx, "NOTICE_tmp")
+ txtOuptut := PathForModuleOut(ctx, "NOTICE_txt", "NOTICE.txt")
+ htmlOutput := PathForModuleOut(ctx, "NOTICE_html", "NOTICE.html")
+ htmlGzOutput := PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
title := "Notices for " + ctx.ModuleName()
ctx.Build(pctx, BuildParams{
- Rule: generateNoticeRule,
- Description: "generate notice output",
- Input: mergedNotice,
- Output: noticeOutput,
+ Rule: generateNoticeRule,
+ Description: "generate notice output",
+ Input: mergedNotice,
+ Output: htmlGzOutput,
+ ImplicitOutputs: WritablePaths{txtOuptut, htmlOutput},
Args: map[string]string{
- "tmpDir": tmpDir.String(),
+ "txtOut": txtOuptut.String(),
+ "htmlOut": htmlOutput.String(),
"title": title,
"inputDir": PathForModuleOut(ctx, "NOTICE_FILES/src").String(),
},
})
- return noticeOutput
+ return NoticeOutputs{
+ Merged: OptionalPathForPath(mergedNotice),
+ TxtOutput: OptionalPathForPath(txtOuptut),
+ HtmlOutput: OptionalPathForPath(htmlOutput),
+ HtmlGzOutput: OptionalPathForPath(htmlGzOutput),
+ }
}
diff --git a/android/paths.go b/android/paths.go
index 20b8b82..0ea4447 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1145,8 +1145,8 @@
partition = ctx.DeviceConfig().OdmPath()
} else if ctx.ProductSpecific() {
partition = ctx.DeviceConfig().ProductPath()
- } else if ctx.ProductServicesSpecific() {
- partition = ctx.DeviceConfig().ProductServicesPath()
+ } else if ctx.SystemExtSpecific() {
+ partition = ctx.DeviceConfig().SystemExtPath()
} else {
partition = "system"
}
diff --git a/android/paths_test.go b/android/paths_test.go
index 7bcfe41..8286e9a 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -294,15 +294,15 @@
out: "target/product/test_device/product/bin/my_test",
},
{
- name: "product_services binary",
+ name: "system_ext binary",
ctx: &moduleInstallPathContextImpl{
baseModuleContext: baseModuleContext{
target: deviceTarget,
- kind: productServicesSpecificModule,
+ kind: systemExtSpecificModule,
},
},
in: []string{"bin", "my_test"},
- out: "target/product/test_device/product_services/bin/my_test",
+ out: "target/product/test_device/system_ext/bin/my_test",
},
{
@@ -354,11 +354,11 @@
},
{
- name: "product_services native test binary",
+ name: "system_ext native test binary",
ctx: &moduleInstallPathContextImpl{
baseModuleContext: baseModuleContext{
target: deviceTarget,
- kind: productServicesSpecificModule,
+ kind: systemExtSpecificModule,
},
inData: true,
},
@@ -415,16 +415,16 @@
},
{
- name: "sanitized product_services binary",
+ name: "sanitized system_ext binary",
ctx: &moduleInstallPathContextImpl{
baseModuleContext: baseModuleContext{
target: deviceTarget,
- kind: productServicesSpecificModule,
+ kind: systemExtSpecificModule,
},
inSanitizerDir: true,
},
in: []string{"bin", "my_test"},
- out: "target/product/test_device/data/asan/product_services/bin/my_test",
+ out: "target/product/test_device/data/asan/system_ext/bin/my_test",
},
{
@@ -479,11 +479,11 @@
out: "target/product/test_device/data/asan/data/nativetest/my_test",
},
{
- name: "sanitized product_services native test binary",
+ name: "sanitized system_ext native test binary",
ctx: &moduleInstallPathContextImpl{
baseModuleContext: baseModuleContext{
target: deviceTarget,
- kind: productServicesSpecificModule,
+ kind: systemExtSpecificModule,
},
inData: true,
inSanitizerDir: true,
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 8d7e74b..e53eb0d 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -267,7 +267,7 @@
func (r *RuleBuilder) Commands() []string {
var commands []string
for _, c := range r.commands {
- commands = append(commands, string(c.buf))
+ commands = append(commands, c.buf.String())
}
return commands
}
@@ -358,7 +358,7 @@
Flag("--output-root").Text(r.sboxOutDir.String()).
Flags(sboxOutputs)
- commandString = string(sboxCmd.buf)
+ commandString = sboxCmd.buf.String()
tools = append(tools, sboxCmd.tools...)
}
@@ -388,7 +388,7 @@
// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
// space as a separator from the previous method.
type RuleBuilderCommand struct {
- buf []byte
+ buf strings.Builder
inputs Paths
outputs WritablePaths
depFiles WritablePaths
@@ -420,10 +420,10 @@
// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
// rule will not have them listed in its dependencies or outputs.
func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
- if len(c.buf) > 0 {
- c.buf = append(c.buf, ' ')
+ if c.buf.Len() > 0 {
+ c.buf.WriteByte(' ')
}
- c.buf = append(c.buf, text...)
+ c.buf.WriteString(text)
return c
}
@@ -608,7 +608,7 @@
// String returns the command line.
func (c *RuleBuilderCommand) String() string {
- return string(c.buf)
+ return c.buf.String()
}
func ninjaNameEscape(s string) string {
diff --git a/android/sh_binary.go b/android/sh_binary.go
index fb7446d..2855aa0 100644
--- a/android/sh_binary.go
+++ b/android/sh_binary.go
@@ -29,6 +29,7 @@
RegisterModuleType("sh_binary", ShBinaryFactory)
RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
RegisterModuleType("sh_test", ShTestFactory)
+ RegisterModuleType("sh_test_host", ShTestHostFactory)
}
type shBinaryProperties struct {
@@ -195,6 +196,7 @@
return module
}
+// sh_test defines a shell script based test module.
func ShTestFactory() Module {
module := &ShTest{}
InitShBinaryModule(&module.ShBinary)
@@ -203,3 +205,13 @@
InitAndroidArchModule(module, HostAndDeviceSupported, MultilibFirst)
return module
}
+
+// sh_test_host defines a shell script based test module that runs on a host.
+func ShTestHostFactory() Module {
+ module := &ShTest{}
+ InitShBinaryModule(&module.ShBinary)
+ module.AddProperties(&module.testProperties)
+
+ InitAndroidArchModule(module, HostSupported, MultilibFirst)
+ return module
+}
diff --git a/android/sh_binary_test.go b/android/sh_binary_test.go
index c99e18c..9df769c 100644
--- a/android/sh_binary_test.go
+++ b/android/sh_binary_test.go
@@ -10,6 +10,7 @@
ctx := NewTestArchContext()
ctx.RegisterModuleType("sh_test", ModuleFactoryAdaptor(ShTestFactory))
+ ctx.RegisterModuleType("sh_test_host", ModuleFactoryAdaptor(ShTestHostFactory))
ctx.Register()
mockFiles := map[string][]byte{
"Android.bp": []byte(bp),
@@ -48,3 +49,23 @@
t.Errorf("Unexpected test data expected: %q, actual: %q", expected, actual)
}
}
+
+func TestShTestHost(t *testing.T) {
+ ctx, _ := testShBinary(t, `
+ sh_test_host {
+ name: "foo",
+ src: "test.sh",
+ filename: "test.sh",
+ data: [
+ "testdata/data1",
+ "testdata/sub/data2",
+ ],
+ }
+ `)
+
+ buildOS := BuildOs.String()
+ mod := ctx.ModuleForTests("foo", buildOS+"_x86_64").Module().(*ShTest)
+ if !mod.Host() {
+ t.Errorf("host bit is not set for a sh_test_host module.")
+ }
+}
diff --git a/android/variable.go b/android/variable.go
index b4f31c6..fcd92d4 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -232,10 +232,10 @@
EnableXOM *bool `json:",omitempty"`
XOMExcludePaths []string `json:",omitempty"`
- VendorPath *string `json:",omitempty"`
- OdmPath *string `json:",omitempty"`
- ProductPath *string `json:",omitempty"`
- ProductServicesPath *string `json:",omitempty"`
+ VendorPath *string `json:",omitempty"`
+ OdmPath *string `json:",omitempty"`
+ ProductPath *string `json:",omitempty"`
+ SystemExtPath *string `json:",omitempty"`
ClangTidy *bool `json:",omitempty"`
TidyChecks *string `json:",omitempty"`
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index af81e43..2def179 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -192,7 +192,7 @@
"LOCAL_VENDOR_MODULE": "vendor",
"LOCAL_ODM_MODULE": "device_specific",
"LOCAL_PRODUCT_MODULE": "product_specific",
- "LOCAL_PRODUCT_SERVICES_MODULE": "product_services_specific",
+ "LOCAL_SYSTEM_EXT_MODULE": "system_ext_specific",
"LOCAL_EXPORT_PACKAGE_RESOURCES": "export_package_resources",
"LOCAL_PRIVILEGED_MODULE": "privileged",
"LOCAL_AAPT_INCLUDE_ALL_RESOURCES": "aapt_include_all_resources",
@@ -602,8 +602,8 @@
return fmt.Errorf("Cannot handle appending to LOCAL_MODULE_PATH")
}
// Analyze value in order to set the correct values for the 'device_specific',
- // 'product_specific', 'product_services_specific' 'vendor'/'soc_specific',
- // 'product_services_specific' attribute. Two cases are allowed:
+ // 'product_specific', 'system_ext_specific' 'vendor'/'soc_specific',
+ // 'system_ext_specific' attribute. Two cases are allowed:
// $(VAR)/<literal-value>
// $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR)/<literal-value>
// The last case is equivalent to $(TARGET_OUT_VENDOR)/<literal-value>
diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go
index 4d5180e..dbb7fde 100644
--- a/androidmk/cmd/androidmk/androidmk_test.go
+++ b/androidmk/cmd/androidmk/androidmk_test.go
@@ -957,37 +957,37 @@
`,
},
{
- desc: "prebuilt_etc_TARGET_OUT_PRODUCT_SERVICES/etc",
+ desc: "prebuilt_etc_TARGET_OUT_SYSTEM_EXT/etc",
in: `
include $(CLEAR_VARS)
LOCAL_MODULE := etc.test1
LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES)/etc/foo/bar
+LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_EXT)/etc/foo/bar
include $(BUILD_PREBUILT)
`,
expected: `
prebuilt_etc {
name: "etc.test1",
sub_dir: "foo/bar",
- product_services_specific: true,
+ system_ext_specific: true,
}
`,
},
{
- desc: "prebuilt_etc_TARGET_OUT_PRODUCT_SERVICES_ETC",
+ desc: "prebuilt_etc_TARGET_OUT_SYSTEM_EXT_ETC",
in: `
include $(CLEAR_VARS)
LOCAL_MODULE := etc.test1
LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES_ETC)/foo/bar
+LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_EXT_ETC)/foo/bar
include $(BUILD_PREBUILT)
`,
expected: `
prebuilt_etc {
name: "etc.test1",
sub_dir: "foo/bar",
- product_services_specific: true,
+ system_ext_specific: true,
}
diff --git a/apex/apex.go b/apex/apex.go
index 4a20936..ee12bd8 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -111,6 +111,7 @@
testTag = dependencyTag{name: "test"}
keyTag = dependencyTag{name: "key"}
certificateTag = dependencyTag{name: "certificate"}
+ usesTag = dependencyTag{name: "uses"}
)
func init() {
@@ -147,6 +148,7 @@
android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.TopDown("apex_deps", apexDepsMutator)
ctx.BottomUp("apex", apexMutator).Parallel()
+ ctx.BottomUp("apex_uses", apexUsesMutator).Parallel()
})
}
@@ -187,6 +189,11 @@
mctx.CreateVariations(apexBundleName)
}
}
+func apexUsesMutator(mctx android.BottomUpMutatorContext) {
+ if ab, ok := mctx.Module().(*apexBundle); ok {
+ mctx.AddFarVariationDependencies(nil, usesTag, ab.properties.Uses...)
+ }
+}
type apexNativeDependencies struct {
// List of native libraries
@@ -272,6 +279,12 @@
// List of sanitizer names that this APEX is enabled for
SanitizerNames []string `blueprint:"mutated"`
+
+ // Indicates this APEX provides C++ shared libaries to other APEXes. Default: false.
+ Provide_cpp_shared_libs *bool
+
+ // List of providing APEXes' names so that this APEX can depend on provided shared libraries.
+ Uses []string
}
type apexTargetBundleProperties struct {
@@ -727,6 +740,30 @@
handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
+ // Check if "uses" requirements are met with dependent apexBundles
+ var providedNativeSharedLibs []string
+ useVendor := proptools.Bool(a.properties.Use_vendor)
+ ctx.VisitDirectDepsBlueprint(func(m blueprint.Module) {
+ if ctx.OtherModuleDependencyTag(m) != usesTag {
+ return
+ }
+ otherName := ctx.OtherModuleName(m)
+ other, ok := m.(*apexBundle)
+ if !ok {
+ ctx.PropertyErrorf("uses", "%q is not a provider", otherName)
+ return
+ }
+ if proptools.Bool(other.properties.Use_vendor) != useVendor {
+ ctx.PropertyErrorf("use_vendor", "%q has different value of use_vendor", otherName)
+ return
+ }
+ if !proptools.Bool(other.properties.Provide_cpp_shared_libs) {
+ ctx.PropertyErrorf("uses", "%q does not provide native_shared_libs", otherName)
+ return
+ }
+ providedNativeSharedLibs = append(providedNativeSharedLibs, other.properties.Native_shared_libs...)
+ })
+
ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
if _, ok := parent.(*apexBundle); ok {
// direct dependencies
@@ -815,6 +852,11 @@
// indirect dependencies
if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && am.IsInstallableToApex() {
if cc, ok := child.(*cc.Module); ok {
+ if android.InList(cc.Name(), providedNativeSharedLibs) {
+ // If we're using a shared library which is provided from other APEX,
+ // don't include it in this APEX
+ return false
+ }
if !a.Host() && (cc.IsStubs() || cc.HasStubsVariants()) {
// If the dependency is a stubs lib, don't include it in this APEX,
// but make sure that the lib is installed on the device.
@@ -905,8 +947,7 @@
return android.OptionalPath{}
}
- return android.OptionalPathForPath(
- android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles)))
+ return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles)).HtmlGzOutput
}
func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType apexPackaging) {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index b410425..94cf19d 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -29,7 +29,32 @@
var buildDir string
+func testApexError(t *testing.T, pattern, bp string) {
+ ctx, config := testApexContext(t, bp)
+ _, 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 testApex(t *testing.T, bp string) *android.TestContext {
+ ctx, config := testApexContext(t, bp)
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+ return ctx
+}
+
+func testApexContext(t *testing.T, bp string) (*android.TestContext, android.Config) {
config := android.TestArchConfig(buildDir, nil)
config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
config.TestProductVariables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test")
@@ -48,6 +73,7 @@
ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.TopDown("apex_deps", apexDepsMutator)
ctx.BottomUp("apex", apexMutator)
+ ctx.BottomUp("apex_uses", apexUsesMutator)
ctx.TopDown("prebuilt_select", android.PrebuiltSelectModuleMutator).Parallel()
ctx.BottomUp("prebuilt_postdeps", android.PrebuiltPostDepsMutator).Parallel()
})
@@ -168,8 +194,10 @@
"system/sepolicy/apex/myapex-file_contexts": nil,
"system/sepolicy/apex/myapex_keytest-file_contexts": nil,
"system/sepolicy/apex/otherapex-file_contexts": nil,
+ "system/sepolicy/apex/commonapex-file_contexts": nil,
"mylib.cpp": nil,
"mytest.cpp": nil,
+ "mylib_common.cpp": nil,
"myprebuilt": nil,
"my_include": nil,
"vendor/foo/devkeys/test.x509.pem": nil,
@@ -188,12 +216,8 @@
"myapex-arm.apex": nil,
"frameworks/base/api/current.txt": nil,
})
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
- return ctx
+ return ctx, config
}
func setUp() {
@@ -210,6 +234,7 @@
// ensure that 'result' contains 'expected'
func ensureContains(t *testing.T, result string, expected string) {
+ t.Helper()
if !strings.Contains(result, expected) {
t.Errorf("%q is not found in %q", expected, result)
}
@@ -217,18 +242,21 @@
// ensures that 'result' does not contain 'notExpected'
func ensureNotContains(t *testing.T, result string, notExpected string) {
+ t.Helper()
if strings.Contains(result, notExpected) {
t.Errorf("%q is found in %q", notExpected, result)
}
}
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 ensureListNotContains(t *testing.T, result []string, notExpected string) {
+ t.Helper()
if android.InList(notExpected, result) {
t.Errorf("%q is found in %v", notExpected, result)
}
@@ -789,6 +817,30 @@
ensureNotContains(t, inputsString, "android_arm64_armv8-a_core_shared_myapex/mylib2.so")
}
+func TestUseVendorFailsIfNotVendorAvailable(t *testing.T) {
+ testApexError(t, `dependency "mylib" of "myapex" missing variant:\n.*image:vendor`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ use_vendor: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+}
+
func TestStaticLinking(t *testing.T) {
ctx := testApex(t, `
apex {
@@ -1321,6 +1373,122 @@
ensureContains(t, copyCmds, "image.apex/bin/test/mytest")
}
+func TestApexUsesOtherApex(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ uses: ["commonapex"],
+ }
+
+ apex {
+ name: "commonapex",
+ key: "myapex.key",
+ native_shared_libs: ["libcommon"],
+ provide_cpp_shared_libs: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["libcommon"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libcommon",
+ srcs: ["mylib_common.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ module1 := ctx.ModuleForTests("myapex", "android_common_myapex")
+ apexRule1 := module1.Rule("apexRule")
+ copyCmds1 := apexRule1.Args["copy_commands"]
+
+ module2 := ctx.ModuleForTests("commonapex", "android_common_commonapex")
+ apexRule2 := module2.Rule("apexRule")
+ copyCmds2 := apexRule2.Args["copy_commands"]
+
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared_myapex")
+ ensureListContains(t, ctx.ModuleVariantsForTests("libcommon"), "android_arm64_armv8-a_core_shared_commonapex")
+ ensureContains(t, copyCmds1, "image.apex/lib64/mylib.so")
+ ensureContains(t, copyCmds2, "image.apex/lib64/libcommon.so")
+ ensureNotContains(t, copyCmds1, "image.apex/lib64/libcommon.so")
+}
+
+func TestApexUsesFailsIfNotProvided(t *testing.T) {
+ testApexError(t, `uses: "commonapex" does not provide native_shared_libs`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ uses: ["commonapex"],
+ }
+
+ apex {
+ name: "commonapex",
+ key: "myapex.key",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `)
+ testApexError(t, `uses: "commonapex" is not a provider`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ uses: ["commonapex"],
+ }
+
+ cc_library {
+ name: "commonapex",
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `)
+}
+
+func TestApexUsesFailsIfUseVenderMismatch(t *testing.T) {
+ testApexError(t, `use_vendor: "commonapex" has different value of use_vendor`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ use_vendor: true,
+ uses: ["commonapex"],
+ }
+
+ apex {
+ name: "commonapex",
+ key: "myapex.key",
+ provide_cpp_shared_libs: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `)
+}
+
func TestMain(m *testing.M) {
run := func() int {
setUp()
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index cac4d9a..5f1cce8 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -508,15 +508,15 @@
"TARGET_OUT": {{prefix: "/usr/share", modType: "prebuilt_usr_share"}, {prefix: "/fonts", modType: "prebuilt_font"},
{prefix: "/etc/firmware", modType: "prebuilt_firmware"}, {prefix: "/vendor/firmware", modType: "prebuilt_firmware", flags: []string{"proprietary"}},
{prefix: "/etc"}},
- "TARGET_OUT_ETC": {{prefix: "/firmware", modType: "prebuilt_firmware"}, {prefix: ""}},
- "TARGET_OUT_PRODUCT": {{prefix: "/etc", flags: []string{"product_specific"}}, {prefix: "/fonts", modType: "prebuilt_font", flags: []string{"product_specific"}}},
- "TARGET_OUT_PRODUCT_ETC": {{prefix: "", flags: []string{"product_specific"}}},
- "TARGET_OUT_ODM": {{prefix: "/etc", flags: []string{"device_specific"}}},
- "TARGET_OUT_PRODUCT_SERVICES": {{prefix: "/etc", flags: []string{"product_services_specific"}}},
- "TARGET_OUT_PRODUCT_SERVICES_ETC": {{prefix: "", flags: []string{"product_services_specific"}}},
- "TARGET_OUT_VENDOR": {{prefix: "/etc", flags: []string{"proprietary"}}, {prefix: "/firmware", modType: "prebuilt_firmware", flags: []string{"proprietary"}}},
- "TARGET_OUT_VENDOR_ETC": {{prefix: "", flags: []string{"proprietary"}}},
- "TARGET_RECOVERY_ROOT_OUT": {{prefix: "/system/etc", flags: []string{"recovery"}}},
+ "TARGET_OUT_ETC": {{prefix: "/firmware", modType: "prebuilt_firmware"}, {prefix: ""}},
+ "TARGET_OUT_PRODUCT": {{prefix: "/etc", flags: []string{"product_specific"}}, {prefix: "/fonts", modType: "prebuilt_font", flags: []string{"product_specific"}}},
+ "TARGET_OUT_PRODUCT_ETC": {{prefix: "", flags: []string{"product_specific"}}},
+ "TARGET_OUT_ODM": {{prefix: "/etc", flags: []string{"device_specific"}}},
+ "TARGET_OUT_SYSTEM_EXT": {{prefix: "/etc", flags: []string{"system_ext_specific"}}},
+ "TARGET_OUT_SYSTEM_EXT_ETC": {{prefix: "", flags: []string{"system_ext_specific"}}},
+ "TARGET_OUT_VENDOR": {{prefix: "/etc", flags: []string{"proprietary"}}, {prefix: "/firmware", modType: "prebuilt_firmware", flags: []string{"proprietary"}}},
+ "TARGET_OUT_VENDOR_ETC": {{prefix: "", flags: []string{"proprietary"}}},
+ "TARGET_RECOVERY_ROOT_OUT": {{prefix: "/system/etc", flags: []string{"recovery"}}},
}
// rewriteAndroidPrebuiltEtc fixes prebuilt_etc rule
diff --git a/cc/cc.go b/cc/cc.go
index 6215067..a0ab255 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -368,6 +368,7 @@
ndkLateStubDepTag = dependencyTag{name: "ndk late stub", library: true}
vndkExtDepTag = dependencyTag{name: "vndk extends", library: true}
runtimeDepTag = dependencyTag{name: "runtime lib"}
+ coverageDepTag = dependencyTag{name: "coverage"}
)
// Module contains the properties and members used by all C/C++ module types, and implements
@@ -974,7 +975,7 @@
flags = c.sanitize.flags(ctx, flags)
}
if c.coverage != nil {
- flags = c.coverage.flags(ctx, flags)
+ flags, deps = c.coverage.flags(ctx, flags, deps)
}
if c.lto != nil {
flags = c.lto.flags(ctx, flags)
diff --git a/cc/coverage.go b/cc/coverage.go
index 9dc7f06..0de0c1c 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -17,6 +17,8 @@
import (
"strconv"
+ "github.com/google/blueprint"
+
"android/soong/android"
)
@@ -41,30 +43,28 @@
return []interface{}{&cov.Properties}
}
-func (cov *coverage) deps(ctx BaseModuleContext, deps Deps) Deps {
- if cov.Properties.NeedCoverageBuild {
- // Link libprofile-extras/libprofile-extras_ndk when coverage
- // variant is required. This is a no-op unless coverage is
- // actually enabled during linking, when
- // '-uinit_profile_extras' is added (in flags()) to force the
- // setup code in libprofile-extras be linked into the
- // binary/library.
- //
- // We cannot narrow it further to only the 'cov' variant since
- // the mutator hasn't run (and we don't have the 'cov' variant
- // yet).
- if !ctx.useSdk() {
- deps.LateStaticLibs = append(deps.LateStaticLibs, "libprofile-extras")
- } else {
- deps.LateStaticLibs = append(deps.LateStaticLibs, "libprofile-extras_ndk")
- }
+func getProfileLibraryName(ctx ModuleContextIntf) string {
+ // This function should only ever be called for a cc.Module, so the
+ // following statement should always succeed.
+ if ctx.useSdk() {
+ return "libprofile-extras_ndk"
+ } else {
+ return "libprofile-extras"
+ }
+}
+
+func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
+ if cov.Properties.NeedCoverageVariant {
+ ctx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "static"},
+ }, coverageDepTag, getProfileLibraryName(ctx))
}
return deps
}
-func (cov *coverage) flags(ctx ModuleContext, flags Flags) Flags {
+func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
if !ctx.DeviceConfig().NativeCoverageEnabled() {
- return flags
+ return flags, deps
}
if cov.Properties.CoverageEnabled {
@@ -114,11 +114,13 @@
if cov.linkCoverage {
flags.LdFlags = append(flags.LdFlags, "--coverage")
- // Force linking of constructor/setup code in libprofile-extras
- flags.LdFlags = append(flags.LdFlags, "-uinit_profile_extras")
+ coverage := ctx.GetDirectDepWithTag(getProfileLibraryName(ctx), coverageDepTag).(*Module)
+ deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
+
+ flags.LdFlags = append(flags.LdFlags, "-Wl,--wrap,getenv")
}
- return flags
+ return flags, deps
}
func (cov *coverage) begin(ctx BaseModuleContext) {
diff --git a/cc/library.go b/cc/library.go
index 5fbb36e..6ac0c39 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -113,8 +113,6 @@
}
type LibraryMutatedProperties struct {
- VariantName string `blueprint:"mutated"`
-
// Build a static variant
BuildStatic bool `blueprint:"mutated"`
// Build a shared variant
@@ -528,7 +526,7 @@
}
}
- return name + library.MutatedProperties.VariantName
+ return name
}
var versioningMacroNamesListMutex sync.Mutex
@@ -633,7 +631,7 @@
library.objects = deps.WholeStaticLibObjs.Copy()
library.objects = library.objects.Append(objs)
- fileName := ctx.ModuleName() + library.MutatedProperties.VariantName + staticLibraryExtension
+ fileName := ctx.ModuleName() + staticLibraryExtension
outputFile := android.PathForModuleOut(ctx, fileName)
builderFlags := flagsToBuilderFlags(flags)
@@ -651,8 +649,7 @@
TransformObjToStaticLib(ctx, library.objects.objFiles, builderFlags, outputFile, objs.tidyFiles)
- library.coverageOutputFile = TransformCoverageFilesToZip(ctx, library.objects,
- ctx.ModuleName()+library.MutatedProperties.VariantName)
+ library.coverageOutputFile = TransformCoverageFilesToZip(ctx, library.objects, ctx.ModuleName())
library.wholeStaticMissingDeps = ctx.GetMissingDependencies()
diff --git a/cmd/diff_target_files/target_files.go b/cmd/diff_target_files/target_files.go
index 8705ca7..0fa04e8 100644
--- a/cmd/diff_target_files/target_files.go
+++ b/cmd/diff_target_files/target_files.go
@@ -28,7 +28,7 @@
"ODM/",
"OEM/",
"PRODUCT/",
- "PRODUCT_SERVICES/",
+ "SYSTEM_EXT/",
"ROOT/",
"SYSTEM/",
"SYSTEM_OTHER/",
diff --git a/java/androidmk.go b/java/androidmk.go
index 39c2d13..90fdd0f 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -327,6 +327,18 @@
install := "$(LOCAL_MODULE_PATH)/" + strings.TrimSuffix(app.installApkName, ".apk") + split.suffix + ".apk"
fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED +=", split.path.String()+":"+install)
}
+ if app.noticeOutputs.Merged.Valid() {
+ fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
+ app.installApkName, app.noticeOutputs.Merged.String(), app.installApkName+"_NOTICE")
+ }
+ if app.noticeOutputs.TxtOutput.Valid() {
+ fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
+ app.installApkName, app.noticeOutputs.TxtOutput.String(), app.installApkName+"_NOTICE.txt")
+ }
+ if app.noticeOutputs.HtmlOutput.Valid() {
+ fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
+ app.installApkName, app.noticeOutputs.HtmlOutput.String(), app.installApkName+"_NOTICE.html")
+ }
},
},
}
diff --git a/java/app.go b/java/app.go
index de291a3..3442658 100644
--- a/java/app.go
+++ b/java/app.go
@@ -124,6 +124,8 @@
installApkName string
additionalAaptFlags []string
+
+ noticeOutputs android.NoticeOutputs
}
func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths {
@@ -340,11 +342,7 @@
return jniJarFile
}
-func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir android.OutputPath) android.OptionalPath {
- if !Bool(a.appProperties.Embed_notices) && !ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
- return android.OptionalPath{}
- }
-
+func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir android.OutputPath) {
// Collect NOTICE files from all dependencies.
seenModules := make(map[android.Module]bool)
noticePathSet := make(map[android.Path]bool)
@@ -374,7 +372,7 @@
}
if len(noticePathSet) == 0 {
- return android.OptionalPath{}
+ return
}
var noticePaths []android.Path
for path := range noticePathSet {
@@ -383,9 +381,8 @@
sort.Slice(noticePaths, func(i, j int) bool {
return noticePaths[i].String() < noticePaths[j].String()
})
- noticeFile := android.BuildNoticeOutput(ctx, installDir, a.installApkName+".apk", noticePaths)
- return android.OptionalPathForPath(noticeFile)
+ a.noticeOutputs = android.BuildNoticeOutput(ctx, installDir, a.installApkName+".apk", noticePaths)
}
// Reads and prepends a main cert from the default cert dir if it hasn't been set already, i.e. it
@@ -438,7 +435,10 @@
installDir = android.PathForModuleInstall(ctx, "app", a.installApkName)
}
- a.aapt.noticeFile = a.noticeBuildActions(ctx, installDir)
+ a.noticeBuildActions(ctx, installDir)
+ if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
+ a.aapt.noticeFile = a.noticeOutputs.HtmlGzOutput
+ }
// Process all building blocks, from AAPT to certificates.
a.aaptBuildActions(ctx)
diff --git a/java/app_test.go b/java/app_test.go
index 9acbd02..f08969d 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1479,16 +1479,20 @@
// bar has NOTICE files to process, but embed_notices is not set.
bar := ctx.ModuleForTests("bar", "android_common")
- mergeNotices = bar.MaybeRule("mergeNoticesRule")
- if mergeNotices.Rule != nil {
- t.Errorf("mergeNotices shouldn't have run for bar")
+ res = bar.Output("package-res.apk")
+ aapt2Flags = res.Args["flags"]
+ e = "-A " + buildDir + "/.intermediates/bar/android_common/NOTICE"
+ if strings.Contains(aapt2Flags, e) {
+ t.Errorf("bar shouldn't have the asset dir flag for NOTICE: %q", e)
}
// baz's embed_notice is true, but it doesn't have any NOTICE files.
baz := ctx.ModuleForTests("baz", "android_common")
- mergeNotices = baz.MaybeRule("mergeNoticesRule")
- if mergeNotices.Rule != nil {
- t.Errorf("mergeNotices shouldn't have run for baz")
+ res = baz.Output("package-res.apk")
+ aapt2Flags = res.Args["flags"]
+ e = "-A " + buildDir + "/.intermediates/baz/android_common/NOTICE"
+ if strings.Contains(aapt2Flags, e) {
+ t.Errorf("baz shouldn't have the asset dir flag for NOTICE: %q", e)
}
}
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index c47f614..8e7f96a 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -111,7 +111,7 @@
productOut("system_other"),
productOut("vendor"),
productOut("product"),
- productOut("product_services"),
+ productOut("system_ext"),
productOut("oem"),
productOut("obj/FAKE"),
productOut("breakpad"),