Merge "Add testOnly attribute to AndroidManifest file of apex_test" am: ac0a9b00a4 am: dc3ffc8c3a

Original change: https://android-review.googlesource.com/c/platform/build/soong/+/1959021

Change-Id: Ia75b0223924a6b38225e881d1d0346533b9984ad
This commit is contained in:
Gurpreet Singh 2022-02-01 15:01:40 +00:00 committed by Automerger Merge Worker
commit 95068d632f
6 changed files with 181 additions and 58 deletions

View file

@ -397,6 +397,22 @@ func (a *apexBundle) buildBundleConfig(ctx android.ModuleContext) android.Output
return output.OutputPath return output.OutputPath
} }
func markManifestTestOnly(ctx android.ModuleContext, androidManifestFile android.Path) android.Path {
return java.ManifestFixer(java.ManifestFixerParams{
Ctx: ctx,
Manifest: androidManifestFile,
SdkContext: nil,
ClassLoaderContexts: nil,
IsLibrary: false,
UseEmbeddedNativeLibs: false,
UsesNonSdkApis: false,
UseEmbeddedDex: false,
HasNoCode: false,
TestOnly: true,
LoggingParent: "",
})
}
// buildUnflattendApex creates build rules to build an APEX using apexer. // buildUnflattendApex creates build rules to build an APEX using apexer.
func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
apexType := a.properties.ApexType apexType := a.properties.ApexType
@ -595,6 +611,11 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
if a.properties.AndroidManifest != nil { if a.properties.AndroidManifest != nil {
androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest)) androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest))
if a.testApex {
androidManifestFile = markManifestTestOnly(ctx, androidManifestFile)
}
implicitInputs = append(implicitInputs, androidManifestFile) implicitInputs = append(implicitInputs, androidManifestFile)
optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String()) optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String())
} }

View file

@ -276,9 +276,19 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon
manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml") manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile) manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, classLoaderContexts, manifestPath := ManifestFixer(ManifestFixerParams{
a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode, Ctx: ctx,
a.LoggingParent) Manifest: manifestSrcPath,
SdkContext: sdkContext,
ClassLoaderContexts: classLoaderContexts,
IsLibrary: a.isLibrary,
UseEmbeddedNativeLibs: a.useEmbeddedNativeLibs,
UsesNonSdkApis: a.usesNonSdkApis,
UseEmbeddedDex: a.useEmbeddedDex,
HasNoCode: a.hasNoCode,
TestOnly: false,
LoggingParent: a.LoggingParent,
})
// Add additional manifest files to transitive manifests. // Add additional manifest files to transitive manifests.
additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests) additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests)

View file

@ -28,13 +28,10 @@ import (
var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer", var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer",
blueprint.RuleParams{ blueprint.RuleParams{
Command: `${config.ManifestFixerCmd} ` + Command: `${config.ManifestFixerCmd} ` +
`--minSdkVersion ${minSdkVersion} ` +
`--targetSdkVersion ${targetSdkVersion} ` +
`--raise-min-sdk-version ` +
`$args $in $out`, `$args $in $out`,
CommandDeps: []string{"${config.ManifestFixerCmd}"}, CommandDeps: []string{"${config.ManifestFixerCmd}"},
}, },
"minSdkVersion", "targetSdkVersion", "args") "args")
var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger", var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger",
blueprint.RuleParams{ blueprint.RuleParams{
@ -58,84 +55,110 @@ func targetSdkVersionForManifestFixer(ctx android.ModuleContext, sdkContext andr
return targetSdkVersion return targetSdkVersion
} }
// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml type ManifestFixerParams struct {
func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext android.SdkContext, Ctx android.ModuleContext
classLoaderContexts dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, Manifest android.Path
useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path { SdkContext android.SdkContext
ClassLoaderContexts dexpreopt.ClassLoaderContextMap
IsLibrary bool
UseEmbeddedNativeLibs bool
UsesNonSdkApis bool
UseEmbeddedDex bool
HasNoCode bool
TestOnly bool
LoggingParent string
}
// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
func ManifestFixer(params ManifestFixerParams) android.Path {
var args []string var args []string
if isLibrary {
if params.IsLibrary {
args = append(args, "--library") args = append(args, "--library")
} else { } else if params.SdkContext != nil {
minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx) minSdkVersion, err := params.SdkContext.MinSdkVersion(params.Ctx).EffectiveVersion(params.Ctx)
if err != nil { if err != nil {
ctx.ModuleErrorf("invalid minSdkVersion: %s", err) params.Ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
} }
if minSdkVersion.FinalOrFutureInt() >= 23 { if minSdkVersion.FinalOrFutureInt() >= 23 {
args = append(args, fmt.Sprintf("--extract-native-libs=%v", !useEmbeddedNativeLibs)) args = append(args, fmt.Sprintf("--extract-native-libs=%v", !params.UseEmbeddedNativeLibs))
} else if useEmbeddedNativeLibs { } else if params.UseEmbeddedNativeLibs {
ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%d doesn't support it", params.Ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%d doesn't support it",
minSdkVersion) minSdkVersion)
} }
} }
if usesNonSdkApis { if params.UsesNonSdkApis {
args = append(args, "--uses-non-sdk-api") args = append(args, "--uses-non-sdk-api")
} }
if useEmbeddedDex { if params.UseEmbeddedDex {
args = append(args, "--use-embedded-dex") args = append(args, "--use-embedded-dex")
} }
// manifest_fixer should add only the implicit SDK libraries inferred by Soong, not those added if params.ClassLoaderContexts != nil {
// explicitly via `uses_libs`/`optional_uses_libs`. // manifest_fixer should add only the implicit SDK libraries inferred by Soong, not those added
requiredUsesLibs, optionalUsesLibs := classLoaderContexts.ImplicitUsesLibs() // explicitly via `uses_libs`/`optional_uses_libs`.
for _, usesLib := range requiredUsesLibs { requiredUsesLibs, optionalUsesLibs := params.ClassLoaderContexts.ImplicitUsesLibs()
args = append(args, "--uses-library", usesLib)
} for _, usesLib := range requiredUsesLibs {
for _, usesLib := range optionalUsesLibs { args = append(args, "--uses-library", usesLib)
args = append(args, "--optional-uses-library", usesLib) }
for _, usesLib := range optionalUsesLibs {
args = append(args, "--optional-uses-library", usesLib)
}
} }
if hasNoCode { if params.HasNoCode {
args = append(args, "--has-no-code") args = append(args, "--has-no-code")
} }
if loggingParent != "" { if params.TestOnly {
args = append(args, "--logging-parent", loggingParent) args = append(args, "--test-only")
}
if params.LoggingParent != "" {
args = append(args, "--logging-parent", params.LoggingParent)
} }
var deps android.Paths var deps android.Paths
targetSdkVersion := targetSdkVersionForManifestFixer(ctx, sdkContext) var argsMapper = make(map[string]string)
if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" { if params.SdkContext != nil {
targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String()) targetSdkVersion := targetSdkVersionForManifestFixer(params.Ctx, params.SdkContext)
deps = append(deps, ApiFingerprintPath(ctx)) args = append(args, "--targetSdkVersion ", targetSdkVersion)
if UseApiFingerprint(params.Ctx) && params.Ctx.ModuleName() != "framework-res" {
targetSdkVersion = params.Ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(params.Ctx).String())
deps = append(deps, ApiFingerprintPath(params.Ctx))
}
minSdkVersion, err := params.SdkContext.MinSdkVersion(params.Ctx).EffectiveVersionString(params.Ctx)
if err != nil {
params.Ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
if UseApiFingerprint(params.Ctx) && params.Ctx.ModuleName() != "framework-res" {
minSdkVersion = params.Ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(params.Ctx).String())
deps = append(deps, ApiFingerprintPath(params.Ctx))
}
if err != nil {
params.Ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
args = append(args, "--minSdkVersion ", minSdkVersion)
args = append(args, "--raise-min-sdk-version")
} }
minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx) fixedManifest := android.PathForModuleOut(params.Ctx, "manifest_fixer", "AndroidManifest.xml")
if err != nil { argsMapper["args"] = strings.Join(args, " ")
ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
deps = append(deps, ApiFingerprintPath(ctx))
}
fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml") params.Ctx.Build(pctx, android.BuildParams{
if err != nil {
ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
ctx.Build(pctx, android.BuildParams{
Rule: manifestFixerRule, Rule: manifestFixerRule,
Description: "fix manifest", Description: "fix manifest",
Input: manifest, Input: params.Manifest,
Implicits: deps, Implicits: deps,
Output: fixedManifest, Output: fixedManifest,
Args: map[string]string{ Args: argsMapper,
"minSdkVersion": minSdkVersion,
"targetSdkVersion": targetSdkVersion,
"args": strings.Join(args, " "),
},
}) })
return fixedManifest.WithoutRel() return fixedManifest.WithoutRel()

View file

@ -2512,7 +2512,7 @@ func TestUsesLibraries(t *testing.T) {
`--uses-library qux ` + `--uses-library qux ` +
`--uses-library quuz ` + `--uses-library quuz ` +
`--uses-library runtime-library` `--uses-library runtime-library`
android.AssertStringEquals(t, "manifest_fixer args", expectManifestFixerArgs, actualManifestFixerArgs) android.AssertStringDoesContain(t, "manifest_fixer args", actualManifestFixerArgs, expectManifestFixerArgs)
// Test that all libraries are verified (library order matters). // Test that all libraries are verified (library order matters).
verifyCmd := app.Rule("verify_uses_libraries").RuleParams.Command verifyCmd := app.Rule("verify_uses_libraries").RuleParams.Command
@ -3055,7 +3055,7 @@ func TestTargetSdkVersionManifestFixer(t *testing.T) {
result := fixture.RunTestWithBp(t, bp) result := fixture.RunTestWithBp(t, bp)
foo := result.ModuleForTests("foo", "android_common") foo := result.ModuleForTests("foo", "android_common")
manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
android.AssertStringEquals(t, testCase.name, testCase.targetSdkVersionExpected, manifestFixerArgs["targetSdkVersion"]) android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion "+testCase.targetSdkVersionExpected)
} }
} }

View file

@ -65,6 +65,9 @@ def parse_args():
parser.add_argument('--has-no-code', dest='has_no_code', action='store_true', parser.add_argument('--has-no-code', dest='has_no_code', action='store_true',
help=('adds hasCode="false" attribute to application. Ignored if application elem ' help=('adds hasCode="false" attribute to application. Ignored if application elem '
'already has a hasCode attribute.')) 'already has a hasCode attribute.'))
parser.add_argument('--test-only', dest='test_only', action='store_true',
help=('adds testOnly="true" attribute to application. Assign true value if application elem '
'already has a testOnly attribute.'))
parser.add_argument('input', help='input AndroidManifest.xml file') parser.add_argument('input', help='input AndroidManifest.xml file')
parser.add_argument('output', help='output AndroidManifest.xml file') parser.add_argument('output', help='output AndroidManifest.xml file')
return parser.parse_args() return parser.parse_args()
@ -318,6 +321,26 @@ def set_has_code_to_false(doc):
attr.value = 'false' attr.value = 'false'
application.setAttributeNode(attr) application.setAttributeNode(attr)
def set_test_only_flag_to_true(doc):
manifest = parse_manifest(doc)
elems = get_children_with_tag(manifest, 'application')
application = elems[0] if len(elems) == 1 else None
if len(elems) > 1:
raise RuntimeError('found multiple <application> tags')
elif not elems:
application = doc.createElement('application')
indent = get_indent(manifest.firstChild, 1)
first = manifest.firstChild
manifest.insertBefore(doc.createTextNode(indent), first)
manifest.insertBefore(application, first)
attr = application.getAttributeNodeNS(android_ns, 'testOnly')
if attr is not None:
# Do nothing If the application already has a testOnly attribute.
return
attr = doc.createAttributeNS(android_ns, 'android:testOnly')
attr.value = 'true'
application.setAttributeNode(attr)
def main(): def main():
"""Program entry point.""" """Program entry point."""
@ -349,6 +372,9 @@ def main():
if args.has_no_code: if args.has_no_code:
set_has_code_to_false(doc) set_has_code_to_false(doc)
if args.test_only:
set_test_only_flag_to_true(doc)
if args.extract_native_libs is not None: if args.extract_native_libs is not None:
add_extract_native_libs(doc, args.extract_native_libs) add_extract_native_libs(doc, args.extract_native_libs)

View file

@ -521,12 +521,55 @@ class AddNoCodeApplicationTest(unittest.TestCase):
self.assert_xml_equal(output, manifest_input) self.assert_xml_equal(output, manifest_input)
def test_has_application_has_code_true(self): def test_has_application_has_code_true(self):
""" Do nothing if there's already an application elemeent even if its """ Do nothing if there's already an application element even if its
hasCode attribute is true. """ hasCode attribute is true. """
manifest_input = self.manifest_tmpl % ' <application android:hasCode="true"/>\n' manifest_input = self.manifest_tmpl % ' <application android:hasCode="true"/>\n'
output = self.run_test(manifest_input) output = self.run_test(manifest_input)
self.assert_xml_equal(output, manifest_input) self.assert_xml_equal(output, manifest_input)
class AddTestOnlyApplicationTest(unittest.TestCase):
"""Unit tests for set_test_only_flag_to_true function."""
def assert_xml_equal(self, output, expected):
self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
def run_test(self, input_manifest):
doc = minidom.parseString(input_manifest)
manifest_fixer.set_test_only_flag_to_true(doc)
output = io.StringIO()
manifest_fixer.write_xml(output, doc)
return output.getvalue()
manifest_tmpl = (
'<?xml version="1.0" encoding="utf-8"?>\n'
'<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
'%s'
'</manifest>\n')
def test_no_application(self):
manifest_input = self.manifest_tmpl % ''
expected = self.manifest_tmpl % ' <application android:testOnly="true"/>\n'
output = self.run_test(manifest_input)
self.assert_xml_equal(output, expected)
def test_has_application_no_test_only(self):
manifest_input = self.manifest_tmpl % ' <application/>\n'
expected = self.manifest_tmpl % ' <application android:testOnly="true"/>\n'
output = self.run_test(manifest_input)
self.assert_xml_equal(output, expected)
def test_has_application_test_only_true(self):
""" If there's already an application element."""
manifest_input = self.manifest_tmpl % ' <application android:testOnly="true"/>\n'
output = self.run_test(manifest_input)
self.assert_xml_equal(output, manifest_input)
def test_has_application_test_only_false(self):
""" If there's already an application element with the testOnly attribute as false."""
manifest_input = self.manifest_tmpl % ' <application android:testOnly="false"/>\n'
output = self.run_test(manifest_input)
self.assert_xml_equal(output, manifest_input)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main(verbosity=2) unittest.main(verbosity=2)