diff options
Diffstat (limited to 'src/main/java/com/google/devtools')
10 files changed, 469 insertions, 250 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidRepositoryRules.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidRepositoryRules.java index 45ecc0301d..d57d1f6c82 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidRepositoryRules.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidRepositoryRules.java @@ -51,7 +51,8 @@ public class AndroidRepositoryRules { "aar_generator", "shuffle_jars", "merge_dexzips", - "debug_keystore"); + "debug_keystore", + "IdlClass"); private static final Function<? super Rule, Map<String, Label>> BINDINGS_FUNCTION = new Function< Rule, Map<String, Label>>() { diff --git a/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspect.java b/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspect.java index a88f4a8646..2db796563d 100644 --- a/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspect.java +++ b/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspect.java @@ -260,6 +260,21 @@ public class AndroidStudioInfoAspect implements ConfiguredAspectFactory { builder.addTransitiveResources(makeArtifactLocation(transitiveResource)); } + boolean hasIdlSources = !provider.getIdlSrcs().isEmpty(); + builder.setHasIdlSources(hasIdlSources); + if (hasIdlSources) { + LibraryArtifact.Builder jarBuilder = LibraryArtifact.newBuilder(); + Artifact idlClassJar = provider.getIdlClassJar(); + if (idlClassJar != null) { + jarBuilder.setJar(makeArtifactLocation(idlClassJar)); + } + Artifact idlSourceJar = provider.getIdlSourceJar(); + if (idlSourceJar != null) { + jarBuilder.setSourceJar(makeArtifactLocation(idlSourceJar)); + } + builder.setIdlJar(jarBuilder.build()); + } + return builder.build(); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java index d28420cfad..e5bac926f7 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java @@ -264,7 +264,6 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { javaSemantics, androidSemantics, resourceApk, - AndroidIdlProvider.EMPTY, ruleContext.getConfiguration().isCodeCoverageEnabled(), true /* collectJavaCompilationArgs */); if (resourceClasses == null) { diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java index b2bf36fbcb..f4e5d67d2b 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java @@ -21,7 +21,6 @@ import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.devtools.build.lib.actions.Artifact; -import com.google.devtools.build.lib.actions.MiddlemanFactory; import com.google.devtools.build.lib.actions.ResourceSet; import com.google.devtools.build.lib.analysis.AnalysisUtils; import com.google.devtools.build.lib.analysis.FilesToRunProvider; @@ -35,7 +34,6 @@ import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; import com.google.devtools.build.lib.analysis.actions.FileWriteAction; import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.analysis.config.BuildConfiguration.StrictDepsMode; -import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; @@ -65,16 +63,13 @@ import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider; import com.google.devtools.build.lib.rules.java.JavaTargetAttributes; import com.google.devtools.build.lib.rules.java.JavaUtil; import com.google.devtools.build.lib.syntax.Type; -import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Set; import javax.annotation.Nullable; @@ -102,12 +97,11 @@ public class AndroidCommon { private Artifact genClassJar; private Artifact genSourceJar; - private Collection<Artifact> idls; - private AndroidIdlProvider transitiveIdlImportData; private NestedSet<ResourceContainer> transitiveResources; - private Map<Artifact, Artifact> translatedIdlSources = ImmutableMap.of(); private boolean asNeverLink; private boolean exportDeps; + private Artifact manifestProtoOutput; + private AndroidIdlHelper idlHelper; public AndroidCommon(RuleContext ruleContext, JavaCommon javaCommon) { this.ruleContext = ruleContext; @@ -212,13 +206,16 @@ public class AndroidCommon { public static AndroidIdeInfoProvider createAndroidIdeInfoProvider( RuleContext ruleContext, AndroidSemantics semantics, + AndroidIdlHelper idlHelper, ResourceApk resourceApk, Artifact zipAlignedApk, Iterable<Artifact> apksUnderTest) { AndroidIdeInfoProvider.Builder ideInfoProviderBuilder = new AndroidIdeInfoProvider.Builder() - .addIdlParcelables(getIdlParcelables(ruleContext)) - .addIdlSrcs(getIdlSrcs(ruleContext)) + .setIdlClassJar(idlHelper.getIdlClassJar()) + .setIdlSourceJar(idlHelper.getIdlSourceJar()) + .addIdlParcelables(idlHelper.getIdlParcelables()) + .addIdlSrcs(idlHelper.getIdlSources()) .addAllApksUnderTest(apksUnderTest); if (zipAlignedApk != null) { @@ -385,17 +382,22 @@ public class AndroidCommon { public JavaTargetAttributes init( JavaSemantics javaSemantics, AndroidSemantics androidSemantics, - ResourceApk resourceApk, AndroidIdlProvider transitiveIdlImportData, + ResourceApk resourceApk, boolean addCoverageSupport, boolean collectJavaCompilationArgs) throws InterruptedException { - ImmutableList<Artifact> extraSources = - resourceApk.isLegacy() || resourceApk.getResourceJavaSrcJar() == null + + classJar = ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_LIBRARY_CLASS_JAR); + idlHelper = new AndroidIdlHelper(ruleContext, classJar); + + ImmutableList.Builder<Artifact> extraSourcesBuilder = ImmutableList.<Artifact>builder() + .addAll(resourceApk.isLegacy() || resourceApk.getResourceJavaSrcJar() == null ? ImmutableList.<Artifact>of() - : ImmutableList.of(resourceApk.getResourceJavaSrcJar()); + : ImmutableList.of(resourceApk.getResourceJavaSrcJar())) + .addAll(idlHelper.getIdlGeneratedJavaSources()); + JavaTargetAttributes.Builder attributes = init( androidSemantics, - transitiveIdlImportData, resourceApk.getTransitiveResources(), - extraSources); + extraSourcesBuilder.build()); JavaCompilationArtifacts.Builder artifactsBuilder = new JavaCompilationArtifacts.Builder(); if (resourceApk.isLegacy()) { compileResources(javaSemantics, artifactsBuilder, attributes, @@ -430,23 +432,11 @@ public class AndroidCommon { private JavaTargetAttributes.Builder init( AndroidSemantics androidSemantics, - AndroidIdlProvider transitiveIdlImportData, NestedSet<AndroidResourcesProvider.ResourceContainer> transitiveResources, Collection<Artifact> extraArtifacts) { - this.transitiveIdlImportData = transitiveIdlImportData; this.transitiveResources = transitiveResources; - - ImmutableList.Builder<Artifact> extraSrcsBuilder = - new ImmutableList.Builder<Artifact>().addAll(extraArtifacts); - - idls = getIdlSrcs(ruleContext); - if (!idls.isEmpty() && !ruleContext.hasErrors()) { - translatedIdlSources = generateTranslatedIdlArtifacts(ruleContext, idls); - } - javaCommon.initializeJavacOpts(androidSemantics.getJavacArguments()); - JavaTargetAttributes.Builder attributes = javaCommon.initCommon( - extraSrcsBuilder.addAll(translatedIdlSources.values()).build()); + JavaTargetAttributes.Builder attributes = javaCommon.initCommon(extraArtifacts); attributes.setBootClassPath(ImmutableList.of( AndroidSdkProvider.fromRuleContext(ruleContext).getAndroidJar())); @@ -533,7 +523,6 @@ public class AndroidCommon { } Artifact jar = null; - classJar = ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_LIBRARY_CLASS_JAR); if (attributes.hasSourceFiles() || attributes.hasSourceJars() || attributes.hasResources()) { // We only want to add a jar to the classpath of a dependent rule if it has content. javaArtifactsBuilder.addRuntimeJar(classJar); @@ -542,7 +531,7 @@ public class AndroidCommon { filesBuilder.add(classJar); - Artifact manifestProtoOutput = helper.createManifestProtoOutput(classJar); + manifestProtoOutput = helper.createManifestProtoOutput(classJar); // The gensrc jar is created only if the target uses annotation processing. Otherwise, // it is null, and the source jar action will not depend on the compile action. @@ -597,17 +586,13 @@ public class AndroidCommon { ResourceApk resourceApk, Artifact zipAlignedApk, Iterable<Artifact> apksUnderTest) { - if (!idls.isEmpty()) { - generateAndroidIdlActions( - ruleContext, idls, transitiveIdlImportData, translatedIdlSources); - } - Runfiles runfiles = new Runfiles.Builder(ruleContext.getWorkspaceName()) .addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES) .build(); javaCommon.addTransitiveInfoProviders(builder, filesToBuild, classJar); javaCommon.addGenJarsProvider(builder, genClassJar, genSourceJar); + idlHelper.addTransitiveInfoProviders(builder, classJar, manifestProtoOutput); return builder .setFilesToBuild(filesToBuild) @@ -621,9 +606,8 @@ public class AndroidCommon { new AndroidResourcesProvider(ruleContext.getLabel(), transitiveResources)) .add( AndroidIdeInfoProvider.class, - createAndroidIdeInfoProvider( - ruleContext, androidSemantics, resourceApk, zipAlignedApk, apksUnderTest)) - .add(AndroidIdlProvider.class, transitiveIdlImportData) + createAndroidIdeInfoProvider(ruleContext, androidSemantics, idlHelper, + resourceApk, zipAlignedApk, apksUnderTest)) .add( JavaCompilationArgsProvider.class, new JavaCompilationArgsProvider( @@ -647,38 +631,6 @@ public class AndroidCommon { Type.STRING)); } - public static ImmutableList<Artifact> getIdlParcelables(RuleContext ruleContext) { - return ruleContext.getRule().isAttrDefined("idl_parcelables", BuildType.LABEL_LIST) - ? ImmutableList.copyOf(ruleContext.getPrerequisiteArtifacts( - "idl_parcelables", Mode.TARGET).filter(AndroidRuleClasses.ANDROID_IDL).list()) - : ImmutableList.<Artifact>of(); - } - - public static Collection<Artifact> getIdlSrcs(RuleContext ruleContext) { - if (!ruleContext.getRule().isAttrDefined("idl_srcs", BuildType.LABEL_LIST)) { - return ImmutableList.of(); - } - checkIdlSrcsSamePackage(ruleContext); - return ruleContext.getPrerequisiteArtifacts( - "idl_srcs", Mode.TARGET).filter(AndroidRuleClasses.ANDROID_IDL).list(); - } - - public static void checkIdlSrcsSamePackage(RuleContext ruleContext) { - PathFragment packageName = ruleContext.getLabel().getPackageFragment(); - Collection<Artifact> idls = ruleContext - .getPrerequisiteArtifacts("idl_srcs", Mode.TARGET) - .filter(AndroidRuleClasses.ANDROID_IDL) - .list(); - for (Artifact idl : idls) { - Label idlLabel = idl.getOwner(); - if (!packageName.equals(idlLabel.getPackageFragment())) { - ruleContext.attributeError("idl_srcs", "do not import '" + idlLabel + "' directly. " - + "You should either move the file to this package or depend on " - + "an appropriate rule there"); - } - } - } - public static NestedSet<LinkerInput> collectTransitiveNativeLibraries( Iterable<? extends TransitiveInfoCollection> deps) { NestedSetBuilder<LinkerInput> builder = NestedSetBuilder.stableOrder(); @@ -733,24 +685,6 @@ public class AndroidCommon { return applicationApksBuilder.build(); } - private ImmutableMap<Artifact, Artifact> generateTranslatedIdlArtifacts( - RuleContext ruleContext, Collection<Artifact> idls) { - ImmutableMap.Builder<Artifact, Artifact> outputJavaSources = ImmutableMap.builder(); - String ruleName = ruleContext.getRule().getName(); - // for each aidl file use aggregated preprocessed files to generate Java code - for (Artifact idl : idls) { - // Reconstruct the package tree under <rule>_aidl to avoid a name conflict - // if the same AIDL files are used in multiple targets. - PathFragment javaOutputPath = FileSystemUtils.replaceExtension( - new PathFragment(ruleName + "_aidl").getRelative(idl.getRootRelativePath()), - ".java"); - Artifact output = ruleContext.getPackageRelativeArtifact( - javaOutputPath, ruleContext.getConfiguration().getGenfilesDirectory()); - outputJavaSources.put(idl, output); - } - return outputJavaSources.build(); - } - private JavaCompilationArgs collectJavaCompilationArgs(RuleContext ruleContext, boolean recursive, boolean neverLink, boolean hasSrcs) { JavaCompilationArgs.Builder builder = JavaCompilationArgs.builder() @@ -762,85 +696,6 @@ public class AndroidCommon { return builder.build(); } - private void generateAndroidIdlActions(RuleContext ruleContext, - Collection<Artifact> idls, AndroidIdlProvider transitiveIdlImportData, - Map<Artifact, Artifact> translatedIdlSources) { - AndroidSdkProvider sdk = AndroidSdkProvider.fromRuleContext(ruleContext); - Set<Artifact> preprocessedIdls = new LinkedHashSet<>(); - List<String> preprocessedArgs = new ArrayList<>(); - - // add imports - for (String idlImport : transitiveIdlImportData.getTransitiveIdlImportRoots()) { - preprocessedArgs.add("-I" + idlImport); - } - - // preprocess each aidl file - preprocessedArgs.add("-p" + sdk.getFrameworkAidl().getExecPathString()); - String ruleName = ruleContext.getRule().getName(); - for (Artifact idl : idls) { - // Reconstruct the package tree under <rule>_aidl to avoid a name conflict - // if the source AIDL files are also generated. - PathFragment preprocessedPath = new PathFragment(ruleName + "_aidl") - .getRelative(idl.getRootRelativePath()); - Artifact preprocessed = ruleContext.getPackageRelativeArtifact( - preprocessedPath, ruleContext.getConfiguration().getGenfilesDirectory()); - preprocessedIdls.add(preprocessed); - preprocessedArgs.add("-p" + preprocessed.getExecPathString()); - - createAndroidIdlPreprocessAction(ruleContext, idl, preprocessed); - } - - // aggregate all preprocessed aidl files - MiddlemanFactory middlemanFactory = ruleContext.getAnalysisEnvironment().getMiddlemanFactory(); - Artifact preprocessedIdlsMiddleman = middlemanFactory.createAggregatingMiddleman( - ruleContext.getActionOwner(), "AndroidIDLMiddleman", preprocessedIdls, - ruleContext.getConfiguration().getMiddlemanDirectory()); - - for (Artifact idl : translatedIdlSources.keySet()) { - createAndroidIdlAction(ruleContext, idl, - transitiveIdlImportData.getTransitiveIdlImports(), - preprocessedIdlsMiddleman, translatedIdlSources.get(idl), preprocessedArgs); - } - } - - private void createAndroidIdlPreprocessAction(RuleContext ruleContext, - Artifact idl, Artifact preprocessed) { - AndroidSdkProvider sdk = AndroidSdkProvider.fromRuleContext(ruleContext); - ruleContext.registerAction(new SpawnAction.Builder() - .setExecutable(sdk.getAidl()) - // Note the below may be an overapproximation of the actual runfiles, due to "conditional - // artifacts" (see Runfiles.PruningManifest). - // TODO(bazel-team): When using getFilesToRun(), the middleman is - // not expanded. Fix by providing code to expand and use getFilesToRun here. - .addInput(idl) - .addOutput(preprocessed) - .addArgument("--preprocess") - .addArgument(preprocessed.getExecPathString()) - .addArgument(idl.getExecPathString()) - .setProgressMessage("Android IDL preprocessing") - .setMnemonic("AndroidIDLPreprocess") - .build(ruleContext)); - } - - private void createAndroidIdlAction(RuleContext ruleContext, - Artifact idl, Iterable<Artifact> idlImports, Artifact preprocessedIdls, - Artifact output, List<String> preprocessedArgs) { - AndroidSdkProvider sdk = AndroidSdkProvider.fromRuleContext(ruleContext); - ruleContext.registerAction(new SpawnAction.Builder() - .setExecutable(sdk.getAidl()) - .addInput(idl) - .addInputs(idlImports) - .addInput(preprocessedIdls) - .addInput(sdk.getFrameworkAidl()) - .addOutput(output) - .addArgument("-b") // Fail if trying to compile a parcelable. - .addArguments(preprocessedArgs) - .addArgument(idl.getExecPathString()) - .addArgument(output.getExecPathString()) - .setProgressMessage("Android IDL generation") - .setMnemonic("AndroidIDLGnerate") - .build(ruleContext)); - } public ImmutableList<String> getJavacOpts() { return javaCommon.getJavacOpts(); diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdeInfoProvider.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdeInfoProvider.java index 5d3ce1c7db..9fe1faf096 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdeInfoProvider.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdeInfoProvider.java @@ -97,6 +97,8 @@ public final class AndroidIdeInfoProvider implements TransitiveInfoProvider { private Artifact manifest = null; private Artifact generatedManifest = null; private Artifact apk = null; + private Artifact idlClassJar = null; + private Artifact idlSourceJar = null; private final Set<SourceDirectory> resourceDirs = new LinkedHashSet<>(); private final Set<SourceDirectory> assetDirs = new LinkedHashSet<>(); private final Set<SourceDirectory> idlDirs = new LinkedHashSet<>(); @@ -108,6 +110,8 @@ public final class AndroidIdeInfoProvider implements TransitiveInfoProvider { manifest, generatedManifest, apk, + idlClassJar, + idlSourceJar, ImmutableList.copyOf(assetDirs), ImmutableList.copyOf(resourceDirs), ImmutableList.copyOf(idlDirs), @@ -133,6 +137,18 @@ public final class AndroidIdeInfoProvider implements TransitiveInfoProvider { return this; } + public Builder setIdlClassJar(@Nullable Artifact idlClassJar) { + Preconditions.checkState(this.idlClassJar == null); + this.idlClassJar = idlClassJar; + return this; + } + + public Builder setIdlSourceJar(@Nullable Artifact idlSourceJar) { + Preconditions.checkState(this.idlSourceJar == null); + this.idlSourceJar = idlSourceJar; + return this; + } + /** * Add "idl_srcs" contents. */ @@ -268,6 +284,8 @@ public final class AndroidIdeInfoProvider implements TransitiveInfoProvider { private final Artifact manifest; private final Artifact generatedManifest; private final Artifact signedApk; + @Nullable private final Artifact idlClassJar; + @Nullable private final Artifact idlSourceJar; private final ImmutableCollection<SourceDirectory> resourceDirs; private final ImmutableCollection<SourceDirectory> assetDirs; private final ImmutableCollection<SourceDirectory> idlImports; @@ -277,6 +295,8 @@ public final class AndroidIdeInfoProvider implements TransitiveInfoProvider { AndroidIdeInfoProvider(@Nullable Artifact manifest, @Nullable Artifact generatedManifest, @Nullable Artifact signedApk, + @Nullable Artifact idlClassJar, + @Nullable Artifact idlSourceJar, ImmutableCollection<SourceDirectory> assetDirs, ImmutableCollection<SourceDirectory> resourceDirs, ImmutableCollection<SourceDirectory> idlImports, @@ -285,6 +305,8 @@ public final class AndroidIdeInfoProvider implements TransitiveInfoProvider { this.manifest = manifest; this.generatedManifest = generatedManifest; this.signedApk = signedApk; + this.idlClassJar = idlClassJar; + this.idlSourceJar = idlSourceJar; this.assetDirs = assetDirs; this.resourceDirs = resourceDirs; this.idlImports = idlImports; @@ -311,6 +333,16 @@ public final class AndroidIdeInfoProvider implements TransitiveInfoProvider { return signedApk; } + @Nullable + public Artifact getIdlClassJar() { + return idlClassJar; + } + + @Nullable + public Artifact getIdlSourceJar() { + return idlSourceJar; + } + /** A list of the direct Resource directories. */ public ImmutableCollection<SourceDirectory> getResourceDirs() { return resourceDirs; diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdlHelper.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdlHelper.java new file mode 100644 index 0000000000..ff4e5f85b4 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdlHelper.java @@ -0,0 +1,373 @@ +// Copyright 2015 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 com.google.devtools.build.lib.rules.android; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.MiddlemanFactory; +import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType; +import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; +import com.google.devtools.build.lib.analysis.actions.SpawnAction; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.packages.BuildType; +import com.google.devtools.build.lib.rules.java.JavaUtil; +import com.google.devtools.build.lib.syntax.Type; +import com.google.devtools.build.lib.vfs.FileSystemUtils; +import com.google.devtools.build.lib.vfs.PathFragment; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * Helper class for Android IDL processing. + */ +public class AndroidIdlHelper { + + private final RuleContext ruleContext; + private final AndroidIdlProvider androidIdlProvider; + private final Collection<Artifact> idls; + private final Map<Artifact, Artifact> translatedIdlSources; + private final Artifact idlClassJar; + private final Artifact idlSourceJar; + + public AndroidIdlHelper(RuleContext ruleContext, Artifact classJar) { + this.ruleContext = ruleContext; + + checkIdlRootImport(ruleContext); + + idls = getIdlSrcs(ruleContext); + + if (!idls.isEmpty() && !ruleContext.hasErrors()) { + translatedIdlSources = generateTranslatedIdlArtifacts(ruleContext, idls); + idlClassJar = createIdlJar(classJar, "-idl.jar"); + idlSourceJar = createIdlJar(classJar, "-idl.srcjar"); + } else { + translatedIdlSources = ImmutableMap.of(); + idlClassJar = null; + idlSourceJar = null; + } + + androidIdlProvider = createAndroidIdlProvider( + ruleContext, idlClassJar, idlSourceJar); + } + + public void addTransitiveInfoProviders(RuleConfiguredTargetBuilder builder, + Artifact classJar, Artifact manifestProtoOutput) { + if (!idls.isEmpty()) { + generateAndroidIdlCompilationActions( + ruleContext, idls, androidIdlProvider, translatedIdlSources); + createIdlClassJarAction(ruleContext, classJar, translatedIdlSources.values(), + manifestProtoOutput, idlClassJar, idlSourceJar); + } + builder + .add(AndroidIdlProvider.class, androidIdlProvider) + .addOutputGroup( + AndroidSemantics.IDL_JARS_OUTPUT_GROUP, androidIdlProvider.getTransitiveIdlJars()); + } + + public Collection<Artifact> getIdlSources() { + return idls; + } + + public Collection<Artifact> getIdlParcelables() { + return getIdlParcelables(ruleContext); + } + + public Collection<Artifact> getIdlGeneratedJavaSources() { + return translatedIdlSources.values(); + } + + @Nullable + public Artifact getIdlClassJar() { + return idlClassJar; + } + + @Nullable + public Artifact getIdlSourceJar() { + return idlSourceJar; + } + + /** + * Returns the artifact for a jar file containing class files that were generated by + * annotation processors. + */ + private Artifact createIdlJar(Artifact outputJar, String suffix) { + return ruleContext.getDerivedArtifact( + FileSystemUtils.replaceExtension(outputJar.getRootRelativePath(), suffix), + outputJar.getRoot()); + } + + private static ImmutableList<Artifact> getIdlParcelables(RuleContext ruleContext) { + return ruleContext.getRule().isAttrDefined("idl_parcelables", BuildType.LABEL_LIST) + ? ImmutableList.copyOf(ruleContext.getPrerequisiteArtifacts( + "idl_parcelables", Mode.TARGET).filter(AndroidRuleClasses.ANDROID_IDL).list()) + : ImmutableList.<Artifact>of(); + } + + private static Collection<Artifact> getIdlSrcs(RuleContext ruleContext) { + if (!ruleContext.getRule().isAttrDefined("idl_srcs", BuildType.LABEL_LIST)) { + return ImmutableList.of(); + } + checkIdlSrcsSamePackage(ruleContext); + return ruleContext.getPrerequisiteArtifacts( + "idl_srcs", Mode.TARGET).filter(AndroidRuleClasses.ANDROID_IDL).list(); + } + + private static void checkIdlSrcsSamePackage(RuleContext ruleContext) { + PathFragment packageName = ruleContext.getLabel().getPackageFragment(); + Collection<Artifact> idls = ruleContext + .getPrerequisiteArtifacts("idl_srcs", Mode.TARGET) + .filter(AndroidRuleClasses.ANDROID_IDL) + .list(); + for (Artifact idl : idls) { + Label idlLabel = idl.getOwner(); + if (!packageName.equals(idlLabel.getPackageFragment())) { + ruleContext.attributeError("idl_srcs", "do not import '" + idlLabel + "' directly. " + + "You should either move the file to this package or depend on " + + "an appropriate rule there"); + } + } + } + + private static ImmutableMap<Artifact, Artifact> generateTranslatedIdlArtifacts( + RuleContext ruleContext, Collection<Artifact> idls) { + ImmutableMap.Builder<Artifact, Artifact> outputJavaSources = ImmutableMap.builder(); + String ruleName = ruleContext.getRule().getName(); + // for each aidl file use aggregated preprocessed files to generate Java code + for (Artifact idl : idls) { + // Reconstruct the package tree under <rule>_aidl to avoid a name conflict + // if the same AIDL files are used in multiple targets. + PathFragment javaOutputPath = FileSystemUtils.replaceExtension( + new PathFragment(ruleName + "_aidl").getRelative(idl.getRootRelativePath()), + ".java"); + Artifact output = ruleContext.getPackageRelativeArtifact( + javaOutputPath, ruleContext.getConfiguration().getGenfilesDirectory()); + outputJavaSources.put(idl, output); + } + return outputJavaSources.build(); + } + + private static void generateAndroidIdlCompilationActions( + RuleContext ruleContext, + Collection<Artifact> idls, + AndroidIdlProvider transitiveIdlImportData, + Map<Artifact, Artifact> translatedIdlSources) { + AndroidSdkProvider sdk = AndroidSdkProvider.fromRuleContext(ruleContext); + Set<Artifact> preprocessedIdls = new LinkedHashSet<>(); + List<String> preprocessedArgs = new ArrayList<>(); + + // add imports + for (String idlImport : transitiveIdlImportData.getTransitiveIdlImportRoots()) { + preprocessedArgs.add("-I" + idlImport); + } + + // preprocess each aidl file + preprocessedArgs.add("-p" + sdk.getFrameworkAidl().getExecPathString()); + String ruleName = ruleContext.getRule().getName(); + for (Artifact idl : idls) { + // Reconstruct the package tree under <rule>_aidl to avoid a name conflict + // if the source AIDL files are also generated. + PathFragment preprocessedPath = new PathFragment(ruleName + "_aidl") + .getRelative(idl.getRootRelativePath()); + Artifact preprocessed = ruleContext.getPackageRelativeArtifact( + preprocessedPath, ruleContext.getConfiguration().getGenfilesDirectory()); + preprocessedIdls.add(preprocessed); + preprocessedArgs.add("-p" + preprocessed.getExecPathString()); + + createAndroidIdlPreprocessAction(ruleContext, idl, preprocessed); + } + + // aggregate all preprocessed aidl files + MiddlemanFactory middlemanFactory = ruleContext.getAnalysisEnvironment().getMiddlemanFactory(); + Artifact preprocessedIdlsMiddleman = middlemanFactory.createAggregatingMiddleman( + ruleContext.getActionOwner(), "AndroidIDLMiddleman", preprocessedIdls, + ruleContext.getConfiguration().getMiddlemanDirectory()); + + for (Artifact idl : translatedIdlSources.keySet()) { + createAndroidIdlAction(ruleContext, idl, + transitiveIdlImportData.getTransitiveIdlImports(), + preprocessedIdlsMiddleman, translatedIdlSources.get(idl), preprocessedArgs); + } + } + + /** + * Creates the idl class jar action. + */ + private static void createIdlClassJarAction( + RuleContext ruleContext, + Artifact classJar, + Iterable<Artifact> generatedIdlJavaFiles, + Artifact manifestProtoOutput, + Artifact idlClassJar, + Artifact idlSourceJar) { + ruleContext.registerAction(new SpawnAction.Builder() + .addInput(manifestProtoOutput) + .addInput(classJar) + .addInputs(generatedIdlJavaFiles) + .addOutput(idlClassJar) + .addOutput(idlSourceJar) + .setExecutable(ruleContext.getExecutablePrerequisite("$idlclass", Mode.HOST)) + .setCommandLine(CustomCommandLine.builder() + .addExecPath("--manifest_proto", manifestProtoOutput) + .addExecPath("--class_jar", classJar) + .addExecPath("--output_class_jar", idlClassJar) + .addExecPath("--output_source_jar", idlSourceJar) + .add("--temp_dir").addPath(idlTempDir(ruleContext, idlClassJar)) + .addExecPaths(generatedIdlJavaFiles) + .build()) + .useParameterFile(ParameterFileType.SHELL_QUOTED) + .setProgressMessage("Building idl jars " + idlClassJar.prettyPrint()) + .setMnemonic("AndroidIdlJars") + .build(ruleContext)); + } + + private static PathFragment idlTempDir(RuleContext ruleContext, Artifact outputJar) { + String basename = FileSystemUtils.removeExtension(outputJar.getExecPath().getBaseName()); + return ruleContext.getConfiguration().getBinDirectory().getExecPath() + .getRelative(ruleContext.getUniqueDirectory("_idl")) + .getRelative(basename + "_temp"); + } + + private static void createAndroidIdlPreprocessAction(RuleContext ruleContext, + Artifact idl, Artifact preprocessed) { + AndroidSdkProvider sdk = AndroidSdkProvider.fromRuleContext(ruleContext); + ruleContext.registerAction(new SpawnAction.Builder() + .setExecutable(sdk.getAidl()) + // Note the below may be an overapproximation of the actual runfiles, due to "conditional + // artifacts" (see Runfiles.PruningManifest). + // TODO(bazel-team): When using getFilesToRun(), the middleman is + // not expanded. Fix by providing code to expand and use getFilesToRun here. + .addInput(idl) + .addOutput(preprocessed) + .addArgument("--preprocess") + .addArgument(preprocessed.getExecPathString()) + .addArgument(idl.getExecPathString()) + .setProgressMessage("Android IDL preprocessing") + .setMnemonic("AndroidIDLPreprocess") + .build(ruleContext)); + } + + private static void createAndroidIdlAction(RuleContext ruleContext, + Artifact idl, Iterable<Artifact> idlImports, Artifact preprocessedIdls, + Artifact output, List<String> preprocessedArgs) { + AndroidSdkProvider sdk = AndroidSdkProvider.fromRuleContext(ruleContext); + ruleContext.registerAction(new SpawnAction.Builder() + .setExecutable(sdk.getAidl()) + .addInput(idl) + .addInputs(idlImports) + .addInput(preprocessedIdls) + .addInput(sdk.getFrameworkAidl()) + .addOutput(output) + .addArgument("-b") // Fail if trying to compile a parcelable. + .addArguments(preprocessedArgs) + .addArgument(idl.getExecPathString()) + .addArgument(output.getExecPathString()) + .setProgressMessage("Android IDL generation") + .setMnemonic("AndroidIDLGnerate") + .build(ruleContext)); + } + + /** + * Returns the union of "idl_srcs" and "idl_parcelables", i.e. all .aidl files + * provided by this library that contribute to .aidl --> .java compilation. + */ + private static Collection<Artifact> getIdlImports(RuleContext ruleContext) { + return ImmutableList.<Artifact>builder() + .addAll(getIdlParcelables(ruleContext)) + .addAll(getIdlSrcs(ruleContext)) + .build(); + } + + private static AndroidIdlProvider createAndroidIdlProvider(RuleContext ruleContext, + @Nullable Artifact idlClassJar, @Nullable Artifact idlSourceJar) { + NestedSetBuilder<String> rootsBuilder = NestedSetBuilder.naiveLinkOrder(); + NestedSetBuilder<Artifact> importsBuilder = NestedSetBuilder.naiveLinkOrder(); + NestedSetBuilder<Artifact> jarsBuilder = NestedSetBuilder.stableOrder(); + if (idlClassJar != null) { + jarsBuilder.add(idlClassJar); + } + if (idlSourceJar != null) { + jarsBuilder.add(idlSourceJar); + } + + for (AndroidIdlProvider dep : ruleContext.getPrerequisites( + "deps", Mode.TARGET, AndroidIdlProvider.class)) { + rootsBuilder.addTransitive(dep.getTransitiveIdlImportRoots()); + importsBuilder.addTransitive(dep.getTransitiveIdlImports()); + jarsBuilder.addTransitive(dep.getTransitiveIdlJars()); + } + + Collection<Artifact> idlImports = getIdlImports(ruleContext); + if (!hasExplicitlySpecifiedIdlImportRoot(ruleContext)) { + for (Artifact idlImport : idlImports) { + PathFragment javaRoot = JavaUtil.getJavaRoot(idlImport.getExecPath()); + if (javaRoot == null) { + ruleContext.ruleError("Cannot determine java/javatests root for import " + + idlImport.getExecPathString()); + } else { + rootsBuilder.add(javaRoot.toString()); + } + } + } else { + PathFragment pkgFragment = ruleContext.getLabel().getPackageFragment(); + Set<PathFragment> idlImportRoots = new HashSet<>(); + for (Artifact idlImport : idlImports) { + idlImportRoots.add(idlImport.getRoot().getExecPath() + .getRelative(pkgFragment) + .getRelative(getIdlImportRoot(ruleContext))); + } + for (PathFragment idlImportRoot : idlImportRoots) { + rootsBuilder.add(idlImportRoot.toString()); + } + } + importsBuilder.addAll(idlImports); + + return new AndroidIdlProvider(rootsBuilder.build(), + importsBuilder.build(), jarsBuilder.build()); + } + + private static void checkIdlRootImport(RuleContext ruleContext) { + if (hasExplicitlySpecifiedIdlImportRoot(ruleContext) + && !hasExplicitlySpecifiedIdlSrcsOrParcelables(ruleContext)) { + ruleContext.attributeError("idl_import_root", + "Neither idl_srcs nor idl_parcelables were specified, " + + "but 'idl_import_root' attribute was set"); + } + } + + private static boolean hasExplicitlySpecifiedIdlImportRoot(RuleContext ruleContext) { + return ruleContext.getRule().isAttributeValueExplicitlySpecified("idl_import_root"); + } + + private static boolean hasExplicitlySpecifiedIdlSrcsOrParcelables(RuleContext ruleContext) { + return ruleContext.getRule().isAttributeValueExplicitlySpecified("idl_srcs") + || ruleContext.getRule().isAttributeValueExplicitlySpecified("idl_parcelables"); + } + + private static String getIdlImportRoot(RuleContext ruleContext) { + return ruleContext.attributes().get("idl_import_root", Type.STRING); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdlProvider.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdlProvider.java index 2c98727aa9..c7365db8d2 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdlProvider.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdlProvider.java @@ -29,15 +29,20 @@ public final class AndroidIdlProvider implements TransitiveInfoProvider { public static final AndroidIdlProvider EMPTY = new AndroidIdlProvider( NestedSetBuilder.<String>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER)); private final NestedSet<String> transitiveIdlImportRoots; private final NestedSet<Artifact> transitiveIdlImports; + private final NestedSet<Artifact> transitiveIdlJars; - public AndroidIdlProvider(NestedSet<String> transitiveIdlImportRoots, - NestedSet<Artifact> transitiveIdlImports) { + public AndroidIdlProvider( + NestedSet<String> transitiveIdlImportRoots, + NestedSet<Artifact> transitiveIdlImports, + NestedSet<Artifact> transitiveIdlJars) { this.transitiveIdlImportRoots = transitiveIdlImportRoots; this.transitiveIdlImports = transitiveIdlImports; + this.transitiveIdlJars = transitiveIdlJars; } /** @@ -53,4 +58,11 @@ public final class AndroidIdlProvider implements TransitiveInfoProvider { public NestedSet<Artifact> getTransitiveIdlImports() { return transitiveIdlImports; } + + /** + * The IDL jars in the transitive closure, both class and source jars. + */ + public NestedSet<Artifact> getTransitiveIdlJars() { + return transitiveIdlJars; + } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java index f83553b5ed..67156b0ba8 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java @@ -39,14 +39,11 @@ import com.google.devtools.build.lib.rules.java.JavaSkylarkApiProvider; import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider; import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider; import com.google.devtools.build.lib.rules.java.JavaTargetAttributes; -import com.google.devtools.build.lib.rules.java.JavaUtil; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.Collection; -import java.util.HashSet; import java.util.List; -import java.util.Set; /** * An implementation for the "android_library" rule. @@ -66,7 +63,6 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { List<? extends TransitiveInfoCollection> deps = ruleContext.getPrerequisites("deps", Mode.TARGET); checkResourceInlining(ruleContext); - checkIdlRootImport(ruleContext); NestedSet<AndroidResourcesProvider.ResourceContainer> transitiveResources = AndroidCommon.getTransitiveResourceContainers(ruleContext, true); NestedSetBuilder<Aar> transitiveAars = collectTransitiveAars(ruleContext); @@ -74,11 +70,9 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { AndroidCommon.collectTransitiveNativeLibraries(deps); NestedSet<Artifact> transitiveProguardConfigs = collectTransitiveProguardConfigs(ruleContext); - AndroidIdlProvider transitiveIdlImportData = collectTransitiveIdlImports(ruleContext); JavaCommon javaCommon = new JavaCommon(ruleContext, javaSemantics); AndroidCommon androidCommon = new AndroidCommon(ruleContext, javaCommon); - boolean definesLocalResources = LocalResourceContainer.definesAndroidResources(ruleContext.attributes()); if (definesLocalResources && !LocalResourceContainer.validateRuleContext(ruleContext)) { @@ -116,7 +110,6 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { javaSemantics, androidSemantics, resourceApk, - transitiveIdlImportData, false /* addCoverageSupport */, true /* collectJavaCompilationArgs */); if (javaTargetAttributes == null) { @@ -221,53 +214,6 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { return mergedJar; } - private AndroidIdlProvider collectTransitiveIdlImports(RuleContext ruleContext) { - NestedSetBuilder<String> rootsBuilder = NestedSetBuilder.naiveLinkOrder(); - NestedSetBuilder<Artifact> importsBuilder = NestedSetBuilder.naiveLinkOrder(); - - for (AndroidIdlProvider dep : ruleContext.getPrerequisites( - "deps", Mode.TARGET, AndroidIdlProvider.class)) { - rootsBuilder.addTransitive(dep.getTransitiveIdlImportRoots()); - importsBuilder.addTransitive(dep.getTransitiveIdlImports()); - } - - Collection<Artifact> idlImports = getIdlImports(ruleContext); - if (!hasExplicitlySpecifiedIdlImportRoot(ruleContext)) { - for (Artifact idlImport : idlImports) { - PathFragment javaRoot = JavaUtil.getJavaRoot(idlImport.getExecPath()); - if (javaRoot == null) { - ruleContext.ruleError("Cannot determine java/javatests root for import " - + idlImport.getExecPathString()); - } else { - rootsBuilder.add(javaRoot.toString()); - } - } - } else { - PathFragment pkgFragment = ruleContext.getLabel().getPackageFragment(); - Set<PathFragment> idlImportRoots = new HashSet<>(); - for (Artifact idlImport : idlImports) { - idlImportRoots.add(idlImport.getRoot().getExecPath() - .getRelative(pkgFragment) - .getRelative(getIdlImportRoot(ruleContext))); - } - for (PathFragment idlImportRoot : idlImportRoots) { - rootsBuilder.add(idlImportRoot.toString()); - } - } - importsBuilder.addAll(idlImports); - - return new AndroidIdlProvider(rootsBuilder.build(), importsBuilder.build()); - } - - private void checkIdlRootImport(RuleContext ruleContext) { - if (hasExplicitlySpecifiedIdlImportRoot(ruleContext) - && !hasExplicitlySpecifiedIdlSrcsOrParcelables(ruleContext)) { - ruleContext.attributeError("idl_import_root", - "Neither idl_srcs nor idl_parcelables were specified, " - + "but 'idl_import_root' attribute was set"); - } - } - private void checkResourceInlining(RuleContext ruleContext) { AndroidResourcesProvider resources = AndroidCommon.getAndroidResources(ruleContext); if (resources == null) { @@ -293,30 +239,6 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { return builder; } - private boolean hasExplicitlySpecifiedIdlImportRoot(RuleContext ruleContext) { - return ruleContext.getRule().isAttributeValueExplicitlySpecified("idl_import_root"); - } - - private boolean hasExplicitlySpecifiedIdlSrcsOrParcelables(RuleContext ruleContext) { - return ruleContext.getRule().isAttributeValueExplicitlySpecified("idl_srcs") - || ruleContext.getRule().isAttributeValueExplicitlySpecified("idl_parcelables"); - } - - private String getIdlImportRoot(RuleContext ruleContext) { - return ruleContext.attributes().get("idl_import_root", Type.STRING); - } - - /** - * Returns the union of "idl_srcs" and "idl_parcelables", i.e. all .aidl files - * provided by this library that contribute to .aidl --> .java compilation. - */ - private static Collection<Artifact> getIdlImports(RuleContext ruleContext) { - return ImmutableList.<Artifact>builder() - .addAll(AndroidCommon.getIdlParcelables(ruleContext)) - .addAll(AndroidCommon.getIdlSrcs(ruleContext)) - .build(); - } - private NestedSet<Artifact> collectTransitiveProguardConfigs(RuleContext ruleContext) { NestedSetBuilder<Artifact> specsBuilder = NestedSetBuilder.naiveLinkOrder(); diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java index 5372fc10b0..c1e14989a3 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java @@ -531,6 +531,8 @@ public final class AndroidRuleClasses { // like all the rest of android tools. .add(attr("$jarjar_bin", LABEL).cfg(HOST).exec() .value(env.getLabel("//third_party/java/jarjar:jarjar_bin"))) + .add(attr("$idlclass", LABEL).cfg(HOST).exec() + .value(env.getLabel(Constants.ANDROID_DEP_PREFIX + "IdlClass"))) .build(); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSemantics.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSemantics.java index ea6a4588b2..e11c46038c 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSemantics.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSemantics.java @@ -15,6 +15,7 @@ package com.google.devtools.build.lib.rules.android; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.OutputGroupProvider; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.actions.SpawnAction; @@ -33,6 +34,13 @@ import javax.annotation.Nullable; */ public interface AndroidSemantics { /** + * Name of the output group used for idl jars (the jars containing the class files for sources + * generated from annotation processors). + */ + String IDL_JARS_OUTPUT_GROUP = + OutputGroupProvider.HIDDEN_OUTPUT_GROUP_PREFIX + "idl_jars"; + + /** * Adds transitive info providers for {@code android_binary} and {@code android_library} rules. * @throws InterruptedException */ |