From 5425090a4f006fb0de0a98fbd0dad66706e1638e Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 5 Dec 2017 09:28:08 -0800 Subject: [PATCH] Add java_genrules to use jars as inputs and outputs Add a java_genrule that has the right multilib flags to be a dependency of a java rule. Make java libraries implement SourceFileProducer so that their classes jar can be used as an input to a java_genrule. Allow libs and static_libs dependencies to be a java_genrule. Test: TestJarGenrules Change-Id: Ib1b31ef9c0b7e72eeed2c9ecc4ce8a1088e0b1c9 --- Android.bp | 1 + java/genrule.go | 35 ++++++++++++++++++++ java/java.go | 81 ++++++++++++++++++++++++++++++++--------------- java/java_test.go | 55 ++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 26 deletions(-) create mode 100644 java/genrule.go diff --git a/Android.bp b/Android.bp index 3e09add4a..643e8df37 100644 --- a/Android.bp +++ b/Android.bp @@ -218,6 +218,7 @@ bootstrap_go_package { "java/app.go", "java/builder.go", "java/gen.go", + "java/genrule.go", "java/jacoco.go", "java/java.go", "java/proto.go", diff --git a/java/genrule.go b/java/genrule.go new file mode 100644 index 000000000..80b703055 --- /dev/null +++ b/java/genrule.go @@ -0,0 +1,35 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package java + +import ( + "android/soong/android" + "android/soong/genrule" +) + +func init() { + android.RegisterModuleType("java_genrule", genRuleFactory) +} + +// java_genrule is a genrule that can depend on other java_* objects. +// The cmd may be run multiple times, once for each of the different host/device +// variations. +func genRuleFactory() android.Module { + module := genrule.NewGenRule() + + android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon) + + return module +} diff --git a/java/java.go b/java/java.go index 4355200fb..0a47db02e 100644 --- a/java/java.go +++ b/java/java.go @@ -211,6 +211,12 @@ type Module struct { compiledSrcJars android.Paths } +func (j *Module) Srcs() android.Paths { + return android.Paths{j.implementationJarFile} +} + +var _ android.SourceFileProducer = (*Module)(nil) + type Dependency interface { HeaderJars() android.Paths ImplementationJars() android.Paths @@ -446,6 +452,15 @@ type deps struct { kotlinStdlib android.Paths } +func checkProducesJars(ctx android.ModuleContext, dep android.SourceFileProducer) { + for _, f := range dep.Srcs() { + if f.Ext() != ".jar" { + ctx.ModuleErrorf("genrule %q must generate files ending with .jar to be used as a libs or static_libs dependency", + ctx.OtherModuleName(dep.(blueprint.Module))) + } + } +} + func (j *Module) collectDeps(ctx android.ModuleContext) deps { var deps deps @@ -462,8 +477,46 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { otherName := ctx.OtherModuleName(module) tag := ctx.OtherModuleDependencyTag(module) - dep, _ := module.(Dependency) - if dep == nil { + switch dep := module.(type) { + case Dependency: + switch tag { + case bootClasspathTag: + deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...) + case libTag: + deps.classpath = append(deps.classpath, dep.HeaderJars()...) + case staticLibTag: + deps.classpath = append(deps.classpath, dep.HeaderJars()...) + deps.staticJars = append(deps.staticJars, dep.ImplementationJars()...) + deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...) + case frameworkResTag: + if ctx.ModuleName() == "framework" { + // framework.jar has a one-off dependency on the R.java and Manifest.java files + // generated by framework-res.apk + deps.srcJars = append(deps.srcJars, dep.(*AndroidApp).aaptSrcJar) + } + case kotlinStdlibTag: + deps.kotlinStdlib = dep.HeaderJars() + default: + panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName())) + } + + deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...) + case android.SourceFileProducer: + switch tag { + case libTag: + checkProducesJars(ctx, dep) + deps.classpath = append(deps.classpath, dep.Srcs()...) + case staticLibTag: + checkProducesJars(ctx, dep) + deps.classpath = append(deps.classpath, dep.Srcs()...) + deps.staticJars = append(deps.staticJars, dep.Srcs()...) + deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...) + case android.DefaultsDepTag, android.SourceDepTag: + // Nothing to do + default: + ctx.ModuleErrorf("dependency on genrule %q may only be in srcs, libs, or static_libs", otherName) + } + default: switch tag { case android.DefaultsDepTag, android.SourceDepTag: // Nothing to do @@ -479,31 +532,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { default: ctx.ModuleErrorf("depends on non-java module %q", otherName) } - return } - - switch tag { - case bootClasspathTag: - deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...) - case libTag: - deps.classpath = append(deps.classpath, dep.HeaderJars()...) - case staticLibTag: - deps.classpath = append(deps.classpath, dep.HeaderJars()...) - deps.staticJars = append(deps.staticJars, dep.ImplementationJars()...) - deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...) - case frameworkResTag: - if ctx.ModuleName() == "framework" { - // framework.jar has a one-off dependency on the R.java and Manifest.java files - // generated by framework-res.apk - deps.srcJars = append(deps.srcJars, dep.(*AndroidApp).aaptSrcJar) - } - case kotlinStdlibTag: - deps.kotlinStdlib = dep.HeaderJars() - default: - panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName())) - } - - deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...) }) return deps diff --git a/java/java_test.go b/java/java_test.go index dbecc70bb..58b27c30f 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -67,6 +67,7 @@ func testContext(config android.Config, bp string, ctx.RegisterModuleType("java_import", android.ModuleFactoryAdaptor(ImportFactory)) ctx.RegisterModuleType("java_defaults", android.ModuleFactoryAdaptor(defaultsFactory)) ctx.RegisterModuleType("java_system_modules", android.ModuleFactoryAdaptor(SystemModulesFactory)) + ctx.RegisterModuleType("java_genrule", android.ModuleFactoryAdaptor(genRuleFactory)) ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(genrule.FileGroupFactory)) ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(genrule.GenRuleFactory)) ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators) @@ -775,6 +776,60 @@ func TestSharding(t *testing.T) { } } +func TestJarGenrules(t *testing.T) { + ctx := testJava(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + } + + java_genrule { + name: "jargen", + tool_files: ["b.java"], + cmd: "$(location b.java) $(in) $(out)", + out: ["jargen.jar"], + srcs: [":foo"], + } + + java_library { + name: "bar", + static_libs: ["jargen"], + srcs: ["c.java"], + } + + java_library { + name: "baz", + libs: ["jargen"], + srcs: ["c.java"], + } + `) + + foo := ctx.ModuleForTests("foo", "android_common").Output("javac/foo.jar") + jargen := ctx.ModuleForTests("jargen", "android_common").Output("jargen.jar") + bar := ctx.ModuleForTests("bar", "android_common").Output("javac/bar.jar") + baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar") + barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar") + + if len(jargen.Inputs) != 1 || jargen.Inputs[0].String() != foo.Output.String() { + t.Errorf("expected jargen inputs [%q], got %q", foo.Output.String(), jargen.Inputs.Strings()) + } + + if !strings.Contains(bar.Args["classpath"], jargen.Output.String()) { + t.Errorf("bar classpath %v does not contain %q", bar.Args["classpath"], jargen.Output.String()) + } + + if !strings.Contains(baz.Args["classpath"], jargen.Output.String()) { + t.Errorf("baz classpath %v does not contain %q", baz.Args["classpath"], jargen.Output.String()) + } + + if len(barCombined.Inputs) != 2 || + barCombined.Inputs[0].String() != bar.Output.String() || + barCombined.Inputs[1].String() != jargen.Output.String() { + t.Errorf("bar combined jar inputs %v is not [%q, %q]", + barCombined.Inputs.Strings(), bar.Output.String(), jargen.Output.String()) + } +} + func fail(t *testing.T, errs []error) { if len(errs) > 0 { for _, err := range errs {