From a15790ac1e0703cd910c21cfd2322b8a61a3b559 Mon Sep 17 00:00:00 2001 From: Mark White Date: Tue, 22 Aug 2023 21:28:11 +0000 Subject: [PATCH] java_library support for building headers-only Flag for java_library modules to build just the Turbine headers and skip building an impl jar. Test: go test java Bug: 289776578 Change-Id: Iad0babf951710476bc32df93c25d17065a14ab84 --- android/neverallow.go | 8 +++++++ android/neverallow_test.go | 16 ++++++++++++++ java/androidmk.go | 3 +++ java/base.go | 44 ++++++++++++++++++++++++++++++++++++++ java/java.go | 1 + java/java_test.go | 18 ++++++++++++++++ 6 files changed, 90 insertions(+) diff --git a/android/neverallow.go b/android/neverallow.go index 24031ba39..2be6a74f5 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -60,6 +60,7 @@ func init() { AddNeverAllowRules(createBp2BuildRule()) AddNeverAllowRules(createCcStubsRule()) AddNeverAllowRules(createJavaExcludeStaticLibsRule()) + AddNeverAllowRules(createProhibitHeaderOnlyRule()) } // Add a NeverAllow rule to the set of rules to apply. @@ -264,6 +265,13 @@ func createJavaExcludeStaticLibsRule() Rule { Because("exclude_static_libs property is only allowed for java modules defined in build/soong, libcore, and frameworks/base/api") } +func createProhibitHeaderOnlyRule() Rule { + return NeverAllow(). + Without("name", "framework-minus-apex-headers"). + With("headers_only", "true"). + Because("headers_only can only be used for generating framework-minus-apex headers for non-updatable modules") +} + func neverallowMutator(ctx BottomUpMutatorContext) { m, ok := ctx.Module().(Module) if !ok { diff --git a/android/neverallow_test.go b/android/neverallow_test.go index 2a938b811..b2620ef92 100644 --- a/android/neverallow_test.go +++ b/android/neverallow_test.go @@ -361,6 +361,21 @@ var neverallowTests = []struct { `exclude_static_libs property is only allowed for java modules defined in build/soong, libcore, and frameworks/base/api`, }, }, + // Test for only allowing headers_only for framework-minus-apex-headers + { + name: `"headers_only" outside framework-minus-apex-headers modules`, + fs: map[string][]byte{ + "a/b/Android.bp": []byte(` + java_library { + name: "baz", + headers_only: true, + } + `), + }, + expectedErrors: []string{ + `headers_only can only be used for generating framework-minus-apex headers for non-updatable modules`, + }, + }, } var prepareForNeverAllowTest = GroupFixturePreparers( @@ -451,6 +466,7 @@ type mockJavaLibraryProperties struct { Sdk_version *string Uncompress_dex *bool Exclude_static_libs []string + Headers_only *bool } type mockJavaLibraryModule struct { diff --git a/java/androidmk.go b/java/androidmk.go index 82505e9e3..b7e2d2ff3 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -79,6 +79,9 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { } else if !library.ApexModuleBase.AvailableFor(android.AvailableToPlatform) { // Platform variant. If not available for the platform, we don't need Make module. entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true}) + } else if library.properties.Headers_only { + // If generating headers only then don't expose to Make. + entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true}) } else { entriesList = append(entriesList, android.AndroidMkEntries{ Class: "JAVA_LIBRARIES", diff --git a/java/base.go b/java/base.go index f5eb01c4b..58ba41098 100644 --- a/java/base.go +++ b/java/base.go @@ -192,6 +192,9 @@ type CommonProperties struct { // Additional srcJars tacked in by GeneratedJavaLibraryModule Generated_srcjars []android.Path `android:"mutated"` + + // If true, then only the headers are built and not the implementation jar. + Headers_only bool } // Properties that are specific to device modules. Host module factories should not add these when @@ -574,6 +577,17 @@ func (j *Module) checkPlatformAPI(ctx android.ModuleContext) { } } +func (j *Module) checkHeadersOnly(ctx android.ModuleContext) { + if _, ok := ctx.Module().(android.SdkContext); ok { + headersOnly := proptools.Bool(&j.properties.Headers_only) + installable := proptools.Bool(j.properties.Installable) + + if headersOnly && installable { + ctx.PropertyErrorf("headers_only", "This module has conflicting settings. headers_only is true which, which means this module doesn't generate an implementation jar. However installable is set to true.") + } + } +} + func (j *Module) addHostProperties() { j.AddProperties( &j.properties, @@ -1151,6 +1165,36 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath // final R classes from the app. flags.classpath = append(android.CopyOf(extraClasspathJars), flags.classpath...) + // If compiling headers then compile them and skip the rest + if j.properties.Headers_only { + if srcFiles.HasExt(".kt") { + ctx.ModuleErrorf("Compiling headers_only with .kt not supported") + } + if ctx.Config().IsEnvFalse("TURBINE_ENABLED") || disableTurbine { + ctx.ModuleErrorf("headers_only is enabled but Turbine is disabled.") + } + + _, j.headerJarFile = + j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, + extraCombinedJars) + if ctx.Failed() { + return + } + + ctx.SetProvider(JavaInfoProvider, JavaInfo{ + HeaderJars: android.PathsIfNonNil(j.headerJarFile), + TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars, + TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars, + AidlIncludeDirs: j.exportAidlIncludeDirs, + ExportedPlugins: j.exportedPluginJars, + ExportedPluginClasses: j.exportedPluginClasses, + ExportedPluginDisableTurbine: j.exportedDisableTurbine, + }) + + j.outputFile = j.headerJarFile + return + } + if srcFiles.HasExt(".kt") { // When using kotlin sources turbine is used to generate annotation processor sources, // including for annotation processors that generate API, so we can use turbine for diff --git a/java/java.go b/java/java.go index 70aba8e2c..c60226de6 100644 --- a/java/java.go +++ b/java/java.go @@ -692,6 +692,7 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { } j.checkSdkVersions(ctx) + j.checkHeadersOnly(ctx) if ctx.Device() { j.dexpreopter.installPath = j.dexpreopter.getInstallPath( ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")) diff --git a/java/java_test.go b/java/java_test.go index 6110e21cb..27933c3bf 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -2370,3 +2370,21 @@ func TestJavaLibraryWithResourcesStem(t *testing.T) { t.Errorf("Module output does not contain expected jar %s", "test.jar") } } + +func TestHeadersOnly(t *testing.T) { + ctx, _ := testJava(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + headers_only: true, + } + `) + + turbine := ctx.ModuleForTests("foo", "android_common").Rule("turbine") + if len(turbine.Inputs) != 1 || turbine.Inputs[0].String() != "a.java" { + t.Errorf(`foo inputs %v != ["a.java"]`, turbine.Inputs) + } + + javac := ctx.ModuleForTests("foo", "android_common").MaybeRule("javac") + android.AssertDeepEquals(t, "javac rule", nil, javac.Rule) +}