From c27ab6678bbf25cca5aec485f8ad1ed0bc3b3c83 Mon Sep 17 00:00:00 2001 From: Jaewoong Jung Date: Thu, 30 May 2019 15:51:14 -0700 Subject: [PATCH] Touch up manifest if there's no source code. The new package manager behavior requires packages without source code to have an application element with hasCode attribute set to false in their manifest. With this change, Soong can now automatically insert one for codeless apps. Test: app_test.go, manifest_fixer_test.py Fixes: 124375490 Change-Id: Ied89a8d07c63805ab910859a4f7c45fc1c60bb73 --- java/aar.go | 3 +- java/android_manifest.go | 6 ++- java/app.go | 3 ++ java/app_test.go | 70 ++++++++++++++++++++++++++++++++++ java/java.go | 13 +++---- scripts/manifest_fixer.py | 28 ++++++++++++++ scripts/manifest_fixer_test.py | 42 ++++++++++++++++++++ 7 files changed, 156 insertions(+), 9 deletions(-) diff --git a/java/aar.go b/java/aar.go index 65a7c2a5e..1b84a47b0 100644 --- a/java/aar.go +++ b/java/aar.go @@ -85,6 +85,7 @@ type aapt struct { useEmbeddedDex bool usesNonSdkApis bool sdkLibraries []string + hasNoCode bool splitNames []string splits []split @@ -204,7 +205,7 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext, ex manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile) manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, sdkLibraries, - a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex) + a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode) a.transitiveManifestPaths = append(android.Paths{manifestPath}, transitiveStaticLibManifests...) diff --git a/java/android_manifest.go b/java/android_manifest.go index b5921be77..021883e60 100644 --- a/java/android_manifest.go +++ b/java/android_manifest.go @@ -53,7 +53,7 @@ var optionalUsesLibs = []string{ // Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext, sdkLibraries []string, - isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, useEmbeddedDex bool) android.Path { + isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, useEmbeddedDex, hasNoCode bool) android.Path { var args []string if isLibrary { @@ -87,6 +87,10 @@ func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext } } + if hasNoCode { + args = append(args, "--has-no-code") + } + var deps android.Paths targetSdkVersion := sdkVersionOrDefault(ctx, sdkContext.targetSdkVersion()) if targetSdkVersion == ctx.Config().PlatformSdkCodename() && diff --git a/java/app.go b/java/app.go index 2d817fe58..140d267f7 100644 --- a/java/app.go +++ b/java/app.go @@ -243,6 +243,9 @@ func (a *AndroidApp) shouldEmbedJnis(ctx android.BaseModuleContext) bool { func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { a.aapt.usesNonSdkApis = Bool(a.Module.deviceProperties.Platform_apis) + // Ask manifest_fixer to add or update the application element indicating this app has no code. + a.aapt.hasNoCode = !a.hasCode(ctx) + aaptLinkFlags := []string{} // Add TARGET_AAPT_CHARACTERISTICS values to AAPT link flags if they exist and --product flags were not provided. diff --git a/java/app_test.go b/java/app_test.go index 559afcc7d..ccf22cb06 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -1319,3 +1319,73 @@ func TestUsesLibraries(t *testing.T) { t.Errorf("wanted %q in %q", w, cmd) } } + +func TestCodelessApp(t *testing.T) { + testCases := []struct { + name string + bp string + noCode bool + }{ + { + name: "normal", + bp: ` + android_app { + name: "foo", + srcs: ["a.java"], + } + `, + noCode: false, + }, + { + name: "app without sources", + bp: ` + android_app { + name: "foo", + } + `, + noCode: true, + }, + { + name: "app with libraries", + bp: ` + android_app { + name: "foo", + static_libs: ["lib"], + } + + java_library { + name: "lib", + srcs: ["a.java"], + } + `, + noCode: false, + }, + { + name: "app with sourceless libraries", + bp: ` + android_app { + name: "foo", + static_libs: ["lib"], + } + + java_library { + name: "lib", + } + `, + // TODO(jungjw): this should probably be true + noCode: false, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + ctx := testApp(t, test.bp) + + foo := ctx.ModuleForTests("foo", "android_common") + manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"] + if strings.Contains(manifestFixerArgs, "--has-no-code") != test.noCode { + t.Errorf("unexpected manifest_fixer args: %q", manifestFixerArgs) + } + }) + } +} diff --git a/java/java.go b/java/java.go index 31c6afec8..41e21b1f1 100644 --- a/java/java.go +++ b/java/java.go @@ -966,8 +966,6 @@ func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaB func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { - hasSrcs := false - j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs) deps := j.collectDeps(ctx) @@ -982,9 +980,6 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } srcFiles = j.genSources(ctx, srcFiles, flags) - if len(srcFiles) > 0 { - hasSrcs = true - } srcJars := srcFiles.FilterByExt(".srcjar") srcJars = append(srcJars, deps.srcJars...) @@ -1181,7 +1176,6 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { if len(deps.staticJars) > 0 { jars = append(jars, deps.staticJars...) - hasSrcs = true } manifest := j.overrideManifest @@ -1293,7 +1287,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.implementationAndResourcesJar = implementationAndResourcesJar - if ctx.Device() && hasSrcs && + if ctx.Device() && j.hasCode(ctx) && (Bool(j.properties.Installable) || Bool(j.deviceProperties.Compile_dex)) { // Dex compilation var dexOutputFile android.ModuleOutPath @@ -1498,6 +1492,11 @@ func (j *Module) CompilerDeps() []string { return jdeps } +func (j *Module) hasCode(ctx android.ModuleContext) bool { + srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs) + return len(srcFiles) > 0 || len(ctx.GetDirectDepsWithTag(staticLibTag)) > 0 +} + // // Java libraries (.jar file) // diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py index bb1485132..945bc1832 100755 --- a/scripts/manifest_fixer.py +++ b/scripts/manifest_fixer.py @@ -59,6 +59,9 @@ def parse_args(): default=None, type=lambda x: (str(x).lower() == 'true'), help=('specify if the app wants to use embedded native libraries. Must not conflict ' 'if already declared in the manifest.')) + parser.add_argument('--has-no-code', dest='has_no_code', action='store_true', + help=('adds hasCode="false" attribute to application. Ignored if application elem ' + 'already has a hasCode attribute.')) parser.add_argument('input', help='input AndroidManifest.xml file') parser.add_argument('output', help='output AndroidManifest.xml file') return parser.parse_args() @@ -245,6 +248,28 @@ def add_extract_native_libs(doc, extract_native_libs): (attr.value, value)) +def set_has_code_to_false(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 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, 'hasCode') + if attr is not None: + # Do nothing if the application already has a hasCode attribute. + return + attr = doc.createAttributeNS(android_ns, 'android:hasCode') + attr.value = 'false' + application.setAttributeNode(attr) + + def main(): """Program entry point.""" try: @@ -269,6 +294,9 @@ def main(): if args.use_embedded_dex: add_use_embedded_dex(doc) + if args.has_no_code: + set_has_code_to_false(doc) + if args.extract_native_libs is not None: add_extract_native_libs(doc, args.extract_native_libs) diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py index 20354218c..ea8095e48 100755 --- a/scripts/manifest_fixer_test.py +++ b/scripts/manifest_fixer_test.py @@ -424,5 +424,47 @@ class AddExtractNativeLibsTest(unittest.TestCase): self.assertRaises(RuntimeError, self.run_test, manifest_input, False) +class AddNoCodeApplicationTest(unittest.TestCase): + """Unit tests for set_has_code_to_false function.""" + + def run_test(self, input_manifest): + doc = minidom.parseString(input_manifest) + manifest_fixer.set_has_code_to_false(doc) + output = StringIO.StringIO() + manifest_fixer.write_xml(output, doc) + return output.getvalue() + + manifest_tmpl = ( + '\n' + '\n' + '%s' + '\n') + + def test_no_application(self): + manifest_input = self.manifest_tmpl % '' + expected = self.manifest_tmpl % ' \n' + output = self.run_test(manifest_input) + self.assertEqual(output, expected) + + def test_has_application_no_has_code(self): + manifest_input = self.manifest_tmpl % ' \n' + expected = self.manifest_tmpl % ' \n' + output = self.run_test(manifest_input) + self.assertEqual(output, expected) + + def test_has_application_has_code_false(self): + """ Do nothing if there's already an application elemeent. """ + manifest_input = self.manifest_tmpl % ' \n' + output = self.run_test(manifest_input) + self.assertEqual(output, manifest_input) + + def test_has_application_has_code_true(self): + """ Do nothing if there's already an application elemeent even if its + hasCode attribute is true. """ + manifest_input = self.manifest_tmpl % ' \n' + output = self.run_test(manifest_input) + self.assertEqual(output, manifest_input) + + if __name__ == '__main__': unittest.main(verbosity=2)