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 8624a7422..3c8f84748 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 0f2c1cf9c..bb39c165c 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -1327,3 +1327,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 0bccb67e3..5544f5763 100644 --- a/java/java.go +++ b/java/java.go @@ -969,8 +969,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) @@ -985,9 +983,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...) @@ -1184,7 +1179,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 @@ -1296,7 +1290,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 @@ -1501,6 +1495,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)