diff options
Diffstat (limited to 'src/main')
9 files changed, 608 insertions, 71 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidSemantics.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidSemantics.java index 366bfcfad1..73b3a5c7be 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidSemantics.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidSemantics.java @@ -21,6 +21,7 @@ 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; import com.google.devtools.build.lib.rules.android.AndroidCommon; +import com.google.devtools.build.lib.rules.android.AndroidIdeInfoProvider; import com.google.devtools.build.lib.rules.android.AndroidSemantics; import com.google.devtools.build.lib.rules.android.ApplicationManifest; import com.google.devtools.build.lib.rules.android.ResourceApk; @@ -39,11 +40,18 @@ public class BazelAndroidSemantics implements AndroidSemantics { } @Override - public void addTransitiveInfoProviders(RuleConfiguredTargetBuilder builder, - RuleContext ruleContext, JavaCommon javaCommon, AndroidCommon androidCommon, - Artifact jarWithAllClasses, ResourceApk resourceApk, Artifact zipAlignedApk, - Iterable<Artifact> apksUnderTest) { - } + public void addNonLocalResources( + RuleContext ruleContext, + ResourceApk resourceApk, + AndroidIdeInfoProvider.Builder ideInfoProviderBuilder) {} + + @Override + public void addTransitiveInfoProviders( + RuleConfiguredTargetBuilder builder, + RuleContext ruleContext, + JavaCommon javaCommon, + AndroidCommon androidCommon, + Artifact jarWithAllClasses) {} @Override public ApplicationManifest getManifestForRule(RuleContext ruleContext) { 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 5a32d0a872..ebf01e32ff 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 @@ -33,6 +33,7 @@ import com.google.devtools.build.lib.analysis.actions.BinaryFileWriteAction; 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.ideinfo.androidstudio.AndroidStudioIdeInfo.AndroidRuleIdeInfo; import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.AndroidSdkRuleInfo; import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.ArtifactLocation; import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.JavaRuleIdeInfo; @@ -43,10 +44,14 @@ import com.google.devtools.build.lib.packages.AspectDefinition; import com.google.devtools.build.lib.packages.AspectParameters; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.rules.android.AndroidCommon; +import com.google.devtools.build.lib.rules.android.AndroidIdeInfoProvider; +import com.google.devtools.build.lib.rules.android.AndroidIdeInfoProvider.SourceDirectory; import com.google.devtools.build.lib.rules.android.AndroidSdkProvider; import com.google.devtools.build.lib.rules.java.JavaExportsProvider; import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider; import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider; +import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.protobuf.MessageLite; @@ -81,7 +86,6 @@ public class AndroidStudioInfoAspect implements ConfiguredAspectFactory { @Override public AspectDefinition getDefinition() { return new AspectDefinition.Builder(NAME) - .requireProvider(JavaSourceInfoProvider.class) .attributeAspect("deps", AndroidStudioInfoAspect.class) .build(); } @@ -94,6 +98,7 @@ public class AndroidStudioInfoAspect implements ConfiguredAspectFactory { // Collect ide build files and calculate dependencies. NestedSetBuilder<Label> transitiveDependenciesBuilder = NestedSetBuilder.stableOrder(); NestedSetBuilder<Label> dependenciesBuilder = NestedSetBuilder.stableOrder(); + NestedSetBuilder<SourceDirectory> transitiveResourcesBuilder = NestedSetBuilder.stableOrder(); NestedSetBuilder<Artifact> ideBuildFilesBuilder = NestedSetBuilder.stableOrder(); @@ -106,6 +111,7 @@ public class AndroidStudioInfoAspect implements ConfiguredAspectFactory { for (AndroidStudioInfoFilesProvider depProvider : androidStudioInfoFilesProviders) { ideBuildFilesBuilder.addTransitive(depProvider.getIdeBuildFiles()); transitiveDependenciesBuilder.addTransitive(depProvider.getTransitiveDependencies()); + transitiveResourcesBuilder.addTransitive(depProvider.getTransitiveResources()); } List<? extends TransitiveInfoCollection> deps = ruleContext.getPrerequisites("deps", Mode.TARGET); @@ -126,12 +132,20 @@ public class AndroidStudioInfoAspect implements ConfiguredAspectFactory { RuleIdeInfo.Kind ruleKind = getRuleKind(ruleContext.getRule(), base); + NestedSet<SourceDirectory> transitiveResources; if (ruleKind != RuleIdeInfo.Kind.UNRECOGNIZED) { - Artifact ideBuildFile = - createIdeBuildArtifact(base, ruleContext, ruleKind, + Pair<Artifact, NestedSet<SourceDirectory>> ideBuildFile = + createIdeBuildArtifact( + base, + ruleContext, + ruleKind, directDependencies, - transitiveDependencies); - ideBuildFilesBuilder.add(ideBuildFile); + transitiveDependencies, + transitiveResourcesBuilder); + ideBuildFilesBuilder.add(ideBuildFile.first); + transitiveResources = ideBuildFile.second; + } else { + transitiveResources = transitiveResourcesBuilder.build(); } NestedSet<Artifact> ideBuildFiles = ideBuildFilesBuilder.build(); @@ -139,7 +153,8 @@ public class AndroidStudioInfoAspect implements ConfiguredAspectFactory { .addOutputGroup(IDE_BUILD, ideBuildFiles) .addProvider( AndroidStudioInfoFilesProvider.class, - new AndroidStudioInfoFilesProvider(ideBuildFiles, transitiveDependencies)); + new AndroidStudioInfoFilesProvider( + ideBuildFiles, transitiveDependencies, transitiveResources)); return builder.build(); } @@ -160,11 +175,13 @@ public class AndroidStudioInfoAspect implements ConfiguredAspectFactory { return sdkInfoBuilder.build(); } - private Artifact createIdeBuildArtifact( + private Pair<Artifact, NestedSet<SourceDirectory>> createIdeBuildArtifact( ConfiguredTarget base, RuleContext ruleContext, Kind ruleKind, - NestedSet<Label> directDependencies, NestedSet<Label> transitiveDependencies) { + NestedSet<Label> directDependencies, + NestedSet<Label> transitiveDependencies, + NestedSetBuilder<SourceDirectory> transitiveResourcesBuilder) { PathFragment ideBuildFilePath = getOutputFilePath(base, ruleContext); Root genfilesDirectory = ruleContext.getConfiguration().getGenfilesDirectory(); Artifact ideBuildFile = @@ -189,22 +206,69 @@ public class AndroidStudioInfoAspect implements ConfiguredAspectFactory { outputBuilder.addAllDependencies(transform(directDependencies, LABEL_TO_STRING)); outputBuilder.addAllTransitiveDependencies(transform(transitiveDependencies, LABEL_TO_STRING)); + NestedSet<SourceDirectory> transitiveResources = null; if (ruleKind == Kind.JAVA_LIBRARY || ruleKind == Kind.JAVA_IMPORT || ruleKind == Kind.JAVA_TEST || ruleKind == Kind.JAVA_BINARY) { outputBuilder.setJavaRuleIdeInfo(makeJavaRuleIdeInfo(base)); + } else if (ruleKind == Kind.ANDROID_LIBRARY || ruleKind == Kind.ANDROID_BINARY) { + outputBuilder.setJavaRuleIdeInfo(makeJavaRuleIdeInfo(base)); + Pair<AndroidRuleIdeInfo, NestedSet<SourceDirectory>> androidRuleIdeInfo = + makeAndroidRuleIdeInfo(ruleContext, base, transitiveResourcesBuilder); + outputBuilder.setAndroidRuleIdeInfo(androidRuleIdeInfo.first); + transitiveResources = androidRuleIdeInfo.second; } else if (ruleKind == Kind.ANDROID_SDK) { outputBuilder.setAndroidSdkRuleInfo( makeAndroidSdkRuleInfo(ruleContext, base.getProvider(AndroidSdkProvider.class))); } + if (transitiveResources == null) { + transitiveResources = transitiveResourcesBuilder.build(); + } final RuleIdeInfo ruleIdeInfo = outputBuilder.build(); ruleContext.registerAction( makeProtoWriteAction(ruleContext.getActionOwner(), ruleIdeInfo, ideBuildFile)); - return ideBuildFile; + return Pair.of(ideBuildFile, transitiveResources); + } + + private static Pair<AndroidRuleIdeInfo, NestedSet<SourceDirectory>> makeAndroidRuleIdeInfo( + RuleContext ruleContext, + ConfiguredTarget base, + NestedSetBuilder<SourceDirectory> transitiveResourcesBuilder) { + AndroidRuleIdeInfo.Builder builder = AndroidRuleIdeInfo.newBuilder(); + AndroidIdeInfoProvider provider = base.getProvider(AndroidIdeInfoProvider.class); + if (provider.getSignedApk() != null) { + builder.setApk(makeArtifactLocation(provider.getSignedApk())); + } + + if (provider.getManifest() != null) { + builder.setManifest(makeArtifactLocation(provider.getManifest())); + } + + if (provider.getGeneratedManifest() != null) { + builder.setGeneratedManifest(makeArtifactLocation(provider.getGeneratedManifest())); + } + + for (Artifact artifact : provider.getApksUnderTest()) { + builder.addDependencyApk(makeArtifactLocation(artifact)); + } + for (SourceDirectory resourceDir : provider.getResourceDirs()) { + ArtifactLocation artifactLocation = makeArtifactLocation(resourceDir); + builder.addResources(artifactLocation); + transitiveResourcesBuilder.add(resourceDir); + } + + builder.setJavaPackage(AndroidCommon.getJavaPackage(ruleContext)); + + NestedSet<SourceDirectory> transitiveResources = transitiveResourcesBuilder.build(); + for (SourceDirectory transitiveResource : transitiveResources) { + builder.addTransitiveResources(makeArtifactLocation(transitiveResource)); + } + + return Pair.of(builder.build(), transitiveResources); } private static BinaryFileWriteAction makeProtoWriteAction( @@ -228,6 +292,13 @@ public class AndroidStudioInfoAspect implements ConfiguredAspectFactory { .build(); } + private static ArtifactLocation makeArtifactLocation(SourceDirectory resourceDir) { + return ArtifactLocation.newBuilder() + .setRootPath(resourceDir.getRootPath().toString()) + .setRelativePath(resourceDir.getRelativePath().toString()) + .build(); + } + private static JavaRuleIdeInfo makeJavaRuleIdeInfo(ConfiguredTarget base) { JavaRuleIdeInfo.Builder builder = JavaRuleIdeInfo.newBuilder(); JavaRuleOutputJarsProvider outputJarsProvider = @@ -325,21 +396,27 @@ public class AndroidStudioInfoAspect implements ConfiguredAspectFactory { } private RuleIdeInfo.Kind getRuleKind(Rule rule, ConfiguredTarget base) { - RuleIdeInfo.Kind kind; - String ruleClassName = rule.getRuleClassObject().getName(); - if ("java_library".equals(ruleClassName)) { - kind = RuleIdeInfo.Kind.JAVA_LIBRARY; - } else if ("java_import".equals(ruleClassName)) { - kind = Kind.JAVA_IMPORT; - } else if ("java_test".equals(ruleClassName)) { - kind = Kind.JAVA_TEST; - } else if ("java_binary".equals(ruleClassName)) { - kind = Kind.JAVA_BINARY; - } else if (base.getProvider(AndroidSdkProvider.class) != null) { - kind = RuleIdeInfo.Kind.ANDROID_SDK; - } else { - kind = RuleIdeInfo.Kind.UNRECOGNIZED; + switch (rule.getRuleClassObject().getName()) { + case "java_library": + return Kind.JAVA_LIBRARY; + case "java_import": + return Kind.JAVA_IMPORT; + case "java_test": + return Kind.JAVA_TEST; + case "java_binary": + return Kind.JAVA_BINARY; + case "android_library": + return Kind.ANDROID_LIBRARY; + case "android_binary": + return Kind.ANDROID_BINARY; + default: + { + if (base.getProvider(AndroidSdkProvider.class) != null) { + return RuleIdeInfo.Kind.ANDROID_SDK; + } else { + return RuleIdeInfo.Kind.UNRECOGNIZED; + } + } } - return kind; } } diff --git a/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoFilesProvider.java b/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoFilesProvider.java index 0cb61a8998..5d935310d1 100644 --- a/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoFilesProvider.java +++ b/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoFilesProvider.java @@ -19,6 +19,8 @@ import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.rules.android.AndroidIdeInfoProvider; +import com.google.devtools.build.lib.rules.android.AndroidIdeInfoProvider.SourceDirectory; /** * File provider for Android Studio ide build files. @@ -27,11 +29,15 @@ import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; public final class AndroidStudioInfoFilesProvider implements TransitiveInfoProvider { private final NestedSet<Artifact> ideBuildFiles; private final NestedSet<Label> transitiveDependencies; + private final NestedSet<AndroidIdeInfoProvider.SourceDirectory> transitiveResources; public AndroidStudioInfoFilesProvider( - NestedSet<Artifact> ideBuildFiles, NestedSet<Label> transitiveDependencies) { + NestedSet<Artifact> ideBuildFiles, + NestedSet<Label> transitiveDependencies, + NestedSet<SourceDirectory> transitiveResources) { this.ideBuildFiles = ideBuildFiles; this.transitiveDependencies = transitiveDependencies; + this.transitiveResources = transitiveResources; } public NestedSet<Artifact> getIdeBuildFiles() { @@ -41,4 +47,8 @@ public final class AndroidStudioInfoFilesProvider implements TransitiveInfoProvi public NestedSet<Label> getTransitiveDependencies() { return transitiveDependencies; } + + public NestedSet<SourceDirectory> getTransitiveResources() { + return transitiveResources; + } } 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 73e3b12722..81db9ac7cf 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 @@ -57,6 +57,7 @@ import com.google.devtools.build.lib.rules.java.DeployArchiveBuilder; import com.google.devtools.build.lib.rules.java.JavaCommon; import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider; import com.google.devtools.build.lib.rules.java.JavaSemantics; +import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider; import com.google.devtools.build.lib.rules.java.JavaTargetAttributes; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.vfs.PathFragment; @@ -265,7 +266,9 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { Artifact deployJar = createDeployJar(ruleContext, javaSemantics, androidCommon, resourceClasses, ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_BINARY_DEPLOY_JAR)); - return createAndroidBinary(ruleContext, + + return createAndroidBinary( + ruleContext, filesBuilder, deployJar, javaCommon, @@ -506,9 +509,10 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { .add(splitDeployMarker) .build(); - androidCommon.addTransitiveInfoProviders(builder); - androidSemantics.addTransitiveInfoProviders(builder, ruleContext, javaCommon, androidCommon, - jarToDex, resourceApk, zipAlignedApk, apksUnderTest); + androidCommon.addTransitiveInfoProviders( + builder, androidSemantics, resourceApk, zipAlignedApk, apksUnderTest); + androidSemantics.addTransitiveInfoProviders( + builder, ruleContext, javaCommon, androidCommon, jarToDex); if (proguardOutput.mapping != null) { builder.add(ProguardMappingProvider.class, @@ -517,14 +521,20 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { return builder .setFilesToBuild(filesToBuild) - .add(RunfilesProvider.class, RunfilesProvider.simple( - new Runfiles.Builder(ruleContext.getWorkspaceName()) - .addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES) - .addTransitiveArtifacts(filesToBuild) - .build())) - .add(ApkProvider.class, - new ApkProvider(NestedSetBuilder.create(Order.STABLE_ORDER, zipAlignedApk), - coverageMetadata)) + .add( + RunfilesProvider.class, + RunfilesProvider.simple( + new Runfiles.Builder(ruleContext.getWorkspaceName()) + .addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES) + .addTransitiveArtifacts(filesToBuild) + .build())) + .add( + JavaSourceInfoProvider.class, + JavaSourceInfoProvider.fromJavaTargetAttributes(resourceClasses, javaSemantics)) + .add( + ApkProvider.class, + new ApkProvider( + NestedSetBuilder.create(Order.STABLE_ORDER, zipAlignedApk), coverageMetadata)) .add(AndroidPreDexJarProvider.class, new AndroidPreDexJarProvider(jarToDex)) .addOutputGroup("mobile_install_full", fullDeployMarker) .addOutputGroup("mobile_install_incremental", incrementalDeployMarker) 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 86aa0c8072..8a26cbf35f 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 @@ -42,6 +42,7 @@ import com.google.devtools.build.lib.collect.nestedset.Order; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SafeImplicitOutputsFunction; import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceContainer; +import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceType; import com.google.devtools.build.lib.rules.android.AndroidRuleClasses.MultidexMode; import com.google.devtools.build.lib.rules.cpp.CcLinkParams; import com.google.devtools.build.lib.rules.cpp.CcLinkParamsProvider; @@ -58,10 +59,12 @@ import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider; import com.google.devtools.build.lib.rules.java.JavaCompilationArtifacts; import com.google.devtools.build.lib.rules.java.JavaCompilationHelper; import com.google.devtools.build.lib.rules.java.JavaNativeLibraryProvider; +import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider; import com.google.devtools.build.lib.rules.java.JavaRuntimeJarProvider; import com.google.devtools.build.lib.rules.java.JavaSemantics; 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; @@ -207,6 +210,55 @@ public class AndroidCommon { ruleContext.registerAction(builder.build(ruleContext)); } + public static AndroidIdeInfoProvider createAndroidIdeInfoProvider( + RuleContext ruleContext, + AndroidSemantics semantics, + ResourceApk resourceApk, + Artifact zipAlignedApk, + Iterable<Artifact> apksUnderTest) { + AndroidIdeInfoProvider.Builder ideInfoProviderBuilder = + new AndroidIdeInfoProvider.Builder() + .addIdlParcelables(getIdlParcelables(ruleContext)) + .addIdlSrcs(getIdlSrcs(ruleContext)) + .addAllApksUnderTest(apksUnderTest); + + if (zipAlignedApk != null) { + ideInfoProviderBuilder.setApk(zipAlignedApk); + } + + // If the rule defines resources, put those in the IDE info. Otherwise, proxy the data coming + // from the android_resources rule in its direct dependencies, if such a thing exists. + if (LocalResourceContainer.definesAndroidResources(ruleContext.attributes())) { + ideInfoProviderBuilder + .addResourceSources(resourceApk.getPrimaryResource().getArtifacts(ResourceType.RESOURCES)) + .addAssetSources( + resourceApk.getPrimaryResource().getArtifacts(ResourceType.ASSETS), + getAssetDir(ruleContext)) + // Sets the possibly merged manifest and the raw manifest. + .setGeneratedManifest(resourceApk.getPrimaryResource().getManifest()) + .setManifest(ruleContext.getPrerequisiteArtifact("manifest", Mode.TARGET)); + } else { + semantics.addNonLocalResources(ruleContext, resourceApk, ideInfoProviderBuilder); + } + + return ideInfoProviderBuilder.build(); + } + + public static String getJavaPackage(RuleContext ruleContext) { + if (ruleContext.attributes().isAttributeValueExplicitlySpecified("custom_package")) { + return ruleContext.attributes().get("custom_package", Type.STRING); + } else { + PathFragment nameFragment = ruleContext.getRule().getPackage().getNameFragment(); + String packageName = JavaUtil.getJavaFullClassname(nameFragment); + if (packageName != null) { + return packageName; + } else { + // This is a workaround for libraries that don't follow the standard Bazel package format + return nameFragment.getPathString().replace('/', '.'); + } + } + } + Artifact compileDexWithJack( MultidexMode mode, Optional<Artifact> mainDexList, Collection<Artifact> proguardSpecs) { return jackCompilationHelper.compileAsDex(mode, mainDexList, proguardSpecs); @@ -546,7 +598,11 @@ public class AndroidCommon { } public RuleConfiguredTargetBuilder addTransitiveInfoProviders( - RuleConfiguredTargetBuilder builder) { + RuleConfiguredTargetBuilder builder, + AndroidSemantics androidSemantics, + ResourceApk resourceApk, + Artifact zipAlignedApk, + Iterable<Artifact> apksUnderTest) { if (!idls.isEmpty()) { generateAndroidIdlActions( ruleContext, idls, transitiveIdlImportData, translatedIdlSources); @@ -557,6 +613,9 @@ public class AndroidCommon { .build(); javaCommon.addTransitiveInfoProviders(builder, filesToBuild, classJar); + builder.add( + JavaRuleOutputJarsProvider.class, + new JavaRuleOutputJarsProvider(classJar, srcJar, genJar, gensrcJar)); return builder .setFilesToBuild(filesToBuild) @@ -567,6 +626,10 @@ public class AndroidCommon { .add( AndroidResourcesProvider.class, new AndroidResourcesProvider(ruleContext.getLabel(), transitiveResources)) + .add( + AndroidIdeInfoProvider.class, + createAndroidIdeInfoProvider( + ruleContext, androidSemantics, resourceApk, zipAlignedApk, apksUnderTest)) .add(AndroidIdlProvider.class, transitiveIdlImportData) .add( JavaCompilationArgsProvider.class, 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 new file mode 100644 index 0000000000..5474ccbbd1 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdeInfoProvider.java @@ -0,0 +1,338 @@ +// 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.base.Preconditions; +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.Root; +import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.vfs.PathFragment; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Objects; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * An Android target provider to provide Android-specific info to IDEs. + */ +@Immutable +public final class AndroidIdeInfoProvider implements TransitiveInfoProvider { + /** Represents a directory that contains sources, generated or otherwise, for an IDE.*/ + @Immutable + public static class SourceDirectory { + final PathFragment relativePath; + final PathFragment rootPath; + final boolean isSource; + + public SourceDirectory(PathFragment rootPath, PathFragment relativePath, boolean isSource) { + this.rootPath = rootPath; + this.relativePath = relativePath; + this.isSource = isSource; + } + + /** + * The root relative path, {@link Artifact#getRootRelativePath()}. + */ + public PathFragment getRelativePath() { + return relativePath; + } + + /** + * The absolute path of the root that contains this directory, {@link Root#getPath()}. + */ + public PathFragment getRootPath() { + return rootPath; + } + + /** Indicates if the directory is in the gen files tree. */ + boolean isSource() { + return isSource; + } + + @Override + public int hashCode() { + return Objects.hash(relativePath, rootPath, isSource); + } + + @Override + public boolean equals(Object other) { + if (other instanceof SourceDirectory) { + SourceDirectory otherDir = (SourceDirectory) other; + return Objects.equals(rootPath, otherDir.rootPath) + && Objects.equals(relativePath, otherDir.relativePath) + && Objects.equals(isSource, otherDir.isSource); + } + return false; + } + + @Override + public String toString() { + return "SourceDirectory [relativePath=" + relativePath + ", rootPath=" + rootPath + + ", isSource=" + isSource + "]"; + } + } + + /** + * Builder for {@link AndroidIdeInfoProvider} + */ + public static class Builder { + private Artifact manifest = null; + private Artifact generatedManifest = null; + private Artifact apk = null; + private final Set<SourceDirectory> resourceDirs = new LinkedHashSet<>(); + private final Set<SourceDirectory> assetDirs = new LinkedHashSet<>(); + private final Set<SourceDirectory> idlDirs = new LinkedHashSet<>(); + private final Set<Artifact> idlSrcs = new LinkedHashSet<>(); + private final Set<Artifact> apksUnderTest = new LinkedHashSet<>(); + + public AndroidIdeInfoProvider build() { + return new AndroidIdeInfoProvider( + manifest, + generatedManifest, + apk, + ImmutableList.copyOf(assetDirs), + ImmutableList.copyOf(resourceDirs), + ImmutableList.copyOf(idlDirs), + ImmutableList.copyOf(idlSrcs), + ImmutableList.copyOf(apksUnderTest)); + } + + public Builder setApk(Artifact apk) { + Preconditions.checkState(this.apk == null); + this.apk = apk; + return this; + } + + public Builder setManifest(Artifact manifest) { + Preconditions.checkState(this.manifest == null); + this.manifest = manifest; + return this; + } + + public Builder setGeneratedManifest(Artifact manifest) { + Preconditions.checkState(this.generatedManifest == null); + this.generatedManifest = manifest; + return this; + } + + /** + * Add "idl_srcs" contents. + */ + public Builder addIdlSrcs(Collection<Artifact> idlSrcs) { + this.idlSrcs.addAll(idlSrcs); + addIdlDirs(idlSrcs); + return this; + } + + /** + * Add "idl_parcelables" contents. + */ + public Builder addIdlParcelables(Collection<Artifact> idlParcelables) { + addIdlDirs(idlParcelables); + return this; + } + + private void addIdlDirs(Collection<Artifact> idlArtifacts) { + for (Artifact idl : idlArtifacts) { + this.idlDirs.add( + new SourceDirectory( + idl.getRoot().getPath().asFragment(), + idl.getRootRelativePath().getParentDirectory(), + idl.isSourceArtifact())); + } + } + + public Builder addAllResources(Collection<SourceDirectory> resources) { + resourceDirs.addAll(resources); + return this; + } + + public Builder addAllAssets(Collection<SourceDirectory> assets) { + assetDirs.addAll(assets); + return this; + } + + public Builder addResourceSource(Artifact resource) { + resourceDirs.add( + new SourceDirectory( + resource.getRoot().getPath().asFragment(), + trimTo( + resource.getRootRelativePath(), + LocalResourceContainer.Builder.findResourceDir(resource)), + resource.isSourceArtifact())); + return this; + } + + public Builder addResourceSources(Collection<Artifact> resources) { + for (Artifact resource : resources) { + addResourceSource(resource); + } + return this; + } + + public Builder addAssetSources(Collection<Artifact> assets, PathFragment assetDir) { + for (Artifact asset : assets) { + addAssetSource(asset, assetDir); + } + return this; + } + + public Builder addAssetSource(Artifact asset, PathFragment assetDir) { + assetDirs.add( + new SourceDirectory( + asset.getRoot().getPath().asFragment(), + trimTo(asset.getRootRelativePath(), assetDir), + asset.isSourceArtifact())); + return this; + } + + public Builder addAllApksUnderTest(Iterable<Artifact> apks) { + Iterables.addAll(apksUnderTest, apks); + return this; + } + + /** + * Finds the rightmost occurrence of the needle and returns subfragment of the haystack from + * left to the end of the occurrence inclusive of the needle. + * + * <pre> + * `Example: + * Given the haystack: + * res/research/handwriting/res/values/strings.xml + * And the needle: + * res + * Returns: + * res/research/handwriting/res + * </pre> + */ + private static PathFragment trimTo(PathFragment haystack, PathFragment needle) { + if (needle.equals(PathFragment.EMPTY_FRAGMENT)) { + return haystack; + } + // Compute the overlap offset for duplicated parts of the needle. + int[] overlap = new int[needle.segmentCount() + 1]; + // Start overlap at -1, as it will cancel out the increment in the search. + // See http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm for the + // details. + overlap[0] = -1; + for (int i = 0, j = -1; i < needle.segmentCount(); j++, i++, overlap[i] = j) { + while (j >= 0 && !needle.getSegment(i).equals(needle.getSegment(j))) { + // Walk the overlap until the bound is found. + j = overlap[j]; + } + } + // TODO(corysmith): reverse the search algorithm. + // Keep the index of the found so that the rightmost index is taken. + int found = -1; + for (int i = 0, j = 0; i < haystack.segmentCount(); i++) { + + while (j >= 0 && !haystack.getSegment(i).equals(needle.getSegment(j))) { + // Not matching, walk the needle index to attempt another match. + j = overlap[j]; + } + j++; + // Needle index is exhausted, so the needle must match. + if (j == needle.segmentCount()) { + // Record the found index + 1 to be inclusive of the end index. + found = i + 1; + // Subtract one from the needle index to restart the search process + j = j - 1; + } + } + if (found != -1) { + // Return the subsection of the haystack. + return haystack.subFragment(0, found); + } + throw new IllegalArgumentException(String.format("%s was not found in %s", needle, haystack)); + } + } + + private final Artifact manifest; + private final Artifact generatedManifest; + private final Artifact signedApk; + private final ImmutableCollection<SourceDirectory> resourceDirs; + private final ImmutableCollection<SourceDirectory> assetDirs; + private final ImmutableCollection<SourceDirectory> idlImports; + private final ImmutableCollection<Artifact> idlSrcs; + private final ImmutableCollection<Artifact> apksUnderTest; + + AndroidIdeInfoProvider(@Nullable Artifact manifest, + @Nullable Artifact generatedManifest, + @Nullable Artifact signedApk, + ImmutableCollection<SourceDirectory> assetDirs, + ImmutableCollection<SourceDirectory> resourceDirs, + ImmutableCollection<SourceDirectory> idlImports, + ImmutableCollection<Artifact> idlSrcs, + ImmutableCollection<Artifact> apksUnderTest) { + this.manifest = manifest; + this.generatedManifest = generatedManifest; + this.signedApk = signedApk; + this.assetDirs = assetDirs; + this.resourceDirs = resourceDirs; + this.idlImports = idlImports; + this.idlSrcs = idlSrcs; + this.apksUnderTest = apksUnderTest; + } + + /** Returns the direct AndroidManifest. */ + @Nullable + public Artifact getManifest() { + return manifest; + } + + /** Returns the direct generated AndroidManifest. */ + @Nullable + public Artifact getGeneratedManifest() { + return generatedManifest; + } + + + /** Returns the direct debug key signed apk, if there is one. */ + @Nullable + public Artifact getSignedApk() { + return signedApk; + } + + /** A list of the direct Resource directories. */ + public ImmutableCollection<SourceDirectory> getResourceDirs() { + return resourceDirs; + } + + /** A list of the direct Asset directories. */ + public ImmutableCollection<SourceDirectory> getAssetDirs() { + return assetDirs; + } + + /** A list of direct idl directories. */ + public ImmutableCollection<SourceDirectory> getIdlImports() { + return idlImports; + } + + /** A list of sources from the "idl_srcs" attribute. */ + public ImmutableCollection<Artifact> getIdlSrcs() { + return idlSrcs; + } + + /** A list of the APKs related to the app under test, if any. */ + public ImmutableCollection<Artifact> getApksUnderTest() { + return apksUnderTest; + } +} 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 6ab6579f7c..1c69f385c3 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 @@ -14,7 +14,6 @@ package com.google.devtools.build.lib.rules.android; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.ConfiguredTarget; @@ -35,8 +34,10 @@ import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.Reso import com.google.devtools.build.lib.rules.cpp.LinkerInput; import com.google.devtools.build.lib.rules.java.JavaCommon; import com.google.devtools.build.lib.rules.java.JavaNeverlinkInfoProvider; +import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider; import com.google.devtools.build.lib.rules.java.JavaSemantics; 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; @@ -78,12 +79,13 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { JavaCommon javaCommon = new JavaCommon(ruleContext, javaSemantics); AndroidCommon androidCommon = new AndroidCommon(ruleContext, javaCommon); - boolean definesLocalResources = + + boolean definesLocalResources = LocalResourceContainer.definesAndroidResources(ruleContext.attributes()); if (definesLocalResources && !LocalResourceContainer.validateRuleContext(ruleContext)) { return null; } - + final ResourceApk resourceApk; if (definesLocalResources) { ApplicationManifest applicationManifest = androidSemantics.getManifestForRule(ruleContext); @@ -101,6 +103,7 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { null /* versionName */, false, null /* proguardCfgOut */); + } catch (RuleConfigurationException e) { // RuleConfigurations exceptions will only be thrown after the RuleContext is updated. // So, exit. @@ -117,7 +120,7 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { transitiveIdlImportData, false /* addCoverageSupport */, true /* collectJavaCompilationArgs */, - AndroidRuleClasses.ANDROID_LIBRARY_GEN_JAR); + AndroidRuleClasses.ANDROID_LIBRARY_GEN_JAR); if (javaTargetAttributes == null) { return null; } @@ -147,17 +150,7 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { Artifact apk = ruleContext.getImplicitOutputArtifact( AndroidRuleClasses.ANDROID_RESOURCES_APK); - String javaPackage; - if (apk.getExecPath().getFirstSegment(ImmutableSet.of("java", "javatests")) - != PathFragment.INVALID_SEGMENT) { - javaPackage = JavaUtil.getJavaPackageName(apk.getExecPath()); - } else { - // This is a workaround for libraries that don't follow the standard Bazel package format - javaPackage = apk.getRootRelativePath().getPathString().replace('/', '.'); - } - if (ruleContext.attributes().isAttributeValueExplicitlySpecified("custom_package")) { - javaPackage = ruleContext.attributes().get("custom_package", Type.STRING); - } + String javaPackage = AndroidCommon.getJavaPackage(ruleContext); ResourceContainer resourceContainer = new ResourceContainer(ruleContext.getLabel(), javaPackage, null /* renameManifestPackage */, false /* inlinedConstants */, @@ -190,11 +183,10 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { .build(ruleContext); RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext); - androidCommon.addTransitiveInfoProviders(builder); + androidCommon.addTransitiveInfoProviders(builder, androidSemantics, + definesLocalResources ? resourceApk : null, null, ImmutableList.<Artifact>of()); androidSemantics.addTransitiveInfoProviders( - builder, ruleContext, javaCommon, androidCommon, - null, definesLocalResources ? resourceApk : null, - null, ImmutableList.<Artifact>of()); + builder, ruleContext, javaCommon, androidCommon, null); return builder .add(AndroidNativeLibraryProvider.class, @@ -202,6 +194,15 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { .addSkylarkTransitiveInfo(JavaSkylarkApiProvider.NAME, new JavaSkylarkApiProvider()) .add(JavaNeverlinkInfoProvider.class, new JavaNeverlinkInfoProvider(androidCommon.isNeverLink())) + // TODO(ahumesky): The gensrcJar is passed in for the srcJar -- is this a mistake? + .add(JavaRuleOutputJarsProvider.class, + new JavaRuleOutputJarsProvider( + classesJar, + androidCommon.getGensrcJar(), + androidCommon.getGenJar(), + androidCommon.getGensrcJar())) + .add(JavaSourceInfoProvider.class, + JavaSourceInfoProvider.fromJavaTargetAttributes(javaTargetAttributes, javaSemantics)) .add(JavaSourceJarsProvider.class, androidCommon.getJavaSourceJarsProvider()) .add(AndroidCcLinkParamsProvider.class, new AndroidCcLinkParamsProvider(androidCommon.getCcLinkParamsStore())) @@ -367,3 +368,4 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { return ruleContext.getPrerequisiteArtifacts("proguard_specs", Mode.TARGET).list(); } } + 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 f86fd57b14..a25ba935ed 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 @@ -23,6 +23,8 @@ import com.google.devtools.build.lib.rules.java.JavaCompilationArtifacts; import com.google.devtools.build.lib.rules.java.JavaSemantics; import com.google.devtools.build.lib.rules.java.JavaTargetAttributes; +import javax.annotation.Nullable; + /** * Pluggable semantics for Android rules. * @@ -32,12 +34,27 @@ import com.google.devtools.build.lib.rules.java.JavaTargetAttributes; public interface AndroidSemantics { /** * Adds transitive info providers for {@code android_binary} and {@code android_library} rules. - * @throws InterruptedException + * @throws InterruptedException + */ + void addTransitiveInfoProviders( + RuleConfiguredTargetBuilder builder, + RuleContext ruleContext, + JavaCommon javaCommon, + AndroidCommon androidCommon, + Artifact jarWithAllClasses) + throws InterruptedException; + + /** + * Add additional resources to IDE info for {@code android_binary} and {@code android_library} + * + * @param ruleContext rule context for target rule + * @param resourceApk resource apk directly provided by the rule + * @param ideInfoProviderBuilder */ - void addTransitiveInfoProviders(RuleConfiguredTargetBuilder builder, - RuleContext ruleContext, JavaCommon javaCommon, AndroidCommon androidCommon, - Artifact jarWithAllClasses, ResourceApk resourceApk, Artifact zipAlignedApk, - Iterable<Artifact> apksUnderTest) throws InterruptedException; + void addNonLocalResources( + RuleContext ruleContext, + @Nullable ResourceApk resourceApk, + AndroidIdeInfoProvider.Builder ideInfoProviderBuilder); /** * Returns the manifest to be used when compiling a given rule. diff --git a/src/main/protobuf/android_studio_ide_info.proto b/src/main/protobuf/android_studio_ide_info.proto index e81db553fc..ade346000b 100644 --- a/src/main/protobuf/android_studio_ide_info.proto +++ b/src/main/protobuf/android_studio_ide_info.proto @@ -37,6 +37,17 @@ message JavaRuleIdeInfo { repeated ArtifactLocation sources = 4; } +message AndroidRuleIdeInfo { + repeated ArtifactLocation resources = 1; + repeated ArtifactLocation transitive_resources = 2; + ArtifactLocation apk = 3; + repeated ArtifactLocation dependency_apk = 4; + ArtifactLocation manifest = 5; + ArtifactLocation generated_manifest = 6; + string java_package = 7; + bool has_idl_sources = 8; +} + message AndroidSdkRuleInfo { string android_sdk_path = 1; string genfiles_path = 2; @@ -63,9 +74,10 @@ message RuleIdeInfo { repeated string dependencies = 4; repeated string transitive_dependencies = 5; - // kind is one of JAVA_LIBRARY, JAVA_TEST, JAVA_IMPORT - JavaRuleIdeInfo java_rule_ide_info = 6; - // kind is ANDROID_SDK - AndroidSdkRuleInfo android_sdk_rule_info = 7; + AndroidSdkRuleInfo android_sdk_rule_info = 6; + + // kind is one of {JAVA,ANDROID}_{LIBRARY,BINARY,TEST} and JAVA_IMPORT + JavaRuleIdeInfo java_rule_ide_info = 7; + AndroidRuleIdeInfo android_rule_ide_info = 8; } |