aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainer.java
diff options
context:
space:
mode:
authorGravatar Michael Staib <mstaib@google.com>2017-01-04 19:18:12 +0000
committerGravatar John Cater <jcater@google.com>2017-01-04 20:40:17 +0000
commit0b06ac45882d8c23f4f53817a0b6e7b3dffc8957 (patch)
treedd7e0456fd86ec609bfbb5cd6f962c98671083c1 /src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainer.java
parentf21d18e1b2643c5b9814b3f2803619ed15550beb (diff)
Refactor ResourceContainer: make top level and use AutoValue Builder support.
This merges the AndroidResourceContainerBuilder (which it's not even clear is related to the nested ResourceContainer!) into the newly generated ResourceContainer.Builder. It also seemed ridiculous for ResourceContainer to get so large and still be subordinate to AndroidResourcesProvider, especially when it's getting passed around in a lot of other places (look how many imports needed fixing!). This CL makes it its own top level class. This allows for easy modification of an existing instance: call toBuilder on it, set the properties you want set, and then call build. -- PiperOrigin-RevId: 143574468 MOS_MIGRATED_REVID=143574468
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainer.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainer.java321
1 files changed, 321 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainer.java b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainer.java
new file mode 100644
index 0000000000..0dca5ac607
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainer.java
@@ -0,0 +1,321 @@
+// Copyright 2017 The Bazel Authors. 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.auto.value.AutoValue;
+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.analysis.RuleContext;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.rules.java.JavaUtil;
+import com.google.devtools.build.lib.syntax.Type;
+import com.google.devtools.build.lib.util.Preconditions;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import java.util.Objects;
+import javax.annotation.Nullable;
+
+/** The resources contributed by a single target. */
+@AutoValue
+@Immutable
+public abstract class ResourceContainer {
+ /** The type of resource in question: either asset or a resource. */
+ public enum ResourceType {
+ ASSETS("assets"),
+ RESOURCES("resources");
+
+ private final String attribute;
+
+ private ResourceType(String attribute) {
+ this.attribute = attribute;
+ }
+
+ public String getAttribute() {
+ return attribute;
+ }
+ }
+
+ public abstract Label getLabel();
+
+ @Nullable
+ public abstract String getJavaPackage();
+
+ @Nullable
+ public abstract String getRenameManifestPackage();
+
+ public abstract boolean getConstantsInlined();
+
+ @Nullable
+ public abstract Artifact getApk();
+
+ public abstract Artifact getManifest();
+
+ @Nullable
+ public abstract Artifact getJavaSourceJar();
+
+ @Nullable
+ public abstract Artifact getJavaClassJar();
+
+ abstract ImmutableList<Artifact> getAssets();
+
+ abstract ImmutableList<Artifact> getResources();
+
+ public ImmutableList<Artifact> getArtifacts(ResourceType resourceType) {
+ return resourceType == ResourceType.ASSETS ? getAssets() : getResources();
+ }
+
+ public Iterable<Artifact> getArtifacts() {
+ return Iterables.concat(getAssets(), getResources());
+ }
+
+ abstract ImmutableList<PathFragment> getAssetsRoots();
+
+ abstract ImmutableList<PathFragment> getResourcesRoots();
+
+ public ImmutableList<PathFragment> getRoots(ResourceType resourceType) {
+ return resourceType == ResourceType.ASSETS ? getAssetsRoots() : getResourcesRoots();
+ }
+
+ public abstract boolean isManifestExported();
+
+ @Nullable
+ public abstract Artifact getRTxt();
+
+ @Nullable
+ public abstract Artifact getSymbolsTxt();
+
+ // The limited hashCode and equals behavior is necessary to avoid duplication when building with
+ // fat_apk_cpu set. Artifacts generated in different configurations will naturally be different
+ // and non-equal objects, causing the ResourceContainer not to be automatically deduplicated at
+ // the android_binary level.
+ // TODO(bazel-team): deduplicate explicitly and remove hashCode and equals overrides to avoid
+ // breaking "equals means interchangeable"
+ @Override
+ public int hashCode() {
+ return Objects.hash(getLabel(), getRTxt(), getSymbolsTxt());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof ResourceContainer)) {
+ return false;
+ }
+ ResourceContainer other = (ResourceContainer) obj;
+ return Objects.equals(getLabel(), other.getLabel())
+ && Objects.equals(getRTxt(), other.getRTxt())
+ && Objects.equals(getSymbolsTxt(), other.getSymbolsTxt());
+ }
+
+ /** Converts this container back into a builder to create a modified copy. */
+ public abstract Builder toBuilder();
+
+ /** Creates a new builder with default values. */
+ public static Builder builder() {
+ return new AutoValue_ResourceContainer.Builder()
+ .setJavaPackageFrom(Builder.JavaPackageSource.MANIFEST)
+ .setConstantsInlined(false)
+ .setAssets(ImmutableList.<Artifact>of())
+ .setResources(ImmutableList.<Artifact>of())
+ .setAssetsRoots(ImmutableList.<PathFragment>of())
+ .setResourcesRoots(ImmutableList.<PathFragment>of());
+ }
+
+ /**
+ * Creates a new builder with the label, Java package, manifest package override, Java source jar,
+ * and manifest-export switch set according to the given rule.
+ */
+ public static Builder builderFromRule(RuleContext ruleContext) throws InterruptedException {
+ return builder().forRuleContext(ruleContext);
+ }
+
+ /** Builder to construct resource containers. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ /** Enum to determine what to do if a package hasn't been manually set. */
+ public enum JavaPackageSource {
+ /**
+ * Uses the package from the manifest, i.e., the generated ResourceContainer will return null
+ * from {@link ResourceContainer#getJavaPackage()}.
+ */
+ MANIFEST,
+ /**
+ * Uses the package from the path to the source jar (or, if the rule context has it set,
+ * the {@code custom_package} attribute). If the source jar is not under a valid Java root,
+ * this will result in an error being added to the rule context. This can only be used if the
+ * builder was created by {@link ResourceContainer#builderFromRule(RuleContext)}.
+ */
+ SOURCE_JAR_PATH
+ }
+
+ @Nullable private RuleContext ruleContext;
+ @Nullable private JavaPackageSource javaPackageSource;
+
+ private Builder forRuleContext(RuleContext ruleContext) throws InterruptedException {
+ Preconditions.checkNotNull(ruleContext);
+ this.ruleContext = ruleContext;
+ return this.setLabel(ruleContext.getLabel())
+ .setRenameManifestPackage(getRenameManifestPackage(ruleContext))
+ .setJavaSourceJar(
+ ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_JAVA_SOURCE_JAR))
+ .setJavaPackageFrom(JavaPackageSource.SOURCE_JAR_PATH)
+ .setManifestExported(getExportsManifest(ruleContext));
+ }
+
+ /**
+ * Sets the Java package from the given source. Overrides earlier calls to
+ * {@link #setJavaPackageFrom(JavaPackageSource)} or {@link #setJavaPackageFromString(String)}.
+ *
+ * <p>To set the package from {@link JavaPackageSource#SOURCE_JAR_PATH}, this instance must have
+ * been created using {@link ResourceContainer#builderFromRule(RuleContext)}. Also in this case,
+ * the source jar must be set non-{@code null} when the {@link #build()} method is called.
+ * It defaults to the source jar implicit output when creating a builder out of a rule context.
+ */
+ public Builder setJavaPackageFrom(JavaPackageSource javaPackageSource) {
+ Preconditions.checkNotNull(javaPackageSource);
+ Preconditions.checkArgument(
+ !(javaPackageSource == JavaPackageSource.SOURCE_JAR_PATH && ruleContext == null),
+ "setJavaPackageFrom(SOURCE_JAR_PATH) is only permitted when using builderFromRule.");
+ this.javaPackageSource = javaPackageSource;
+ return this.setJavaPackage(null);
+ }
+
+ /**
+ * Sets the Java package from the given string. Overrides earlier calls to
+ * {@link #setJavaPackageFrom(JavaPackageSource)} or {@link #setJavaPackageFromString(String)}.
+ *
+ * <p>To make {@link ResourceContainer#getJavaPackage()} return {@code null}, call
+ * {@code setJavaPackageFrom(MANIFEST)} instead.
+ */
+ public Builder setJavaPackageFromString(String javaPackageOverride) {
+ Preconditions.checkNotNull(javaPackageOverride);
+ this.javaPackageSource = null;
+ return this.setJavaPackage(javaPackageOverride);
+ }
+
+ /**
+ * Sets the assets, resources, asset roots, and resource roots from the given local resource
+ * container.
+ *
+ * <p>This will override any of these values which were previously set directly.
+ */
+ public Builder setAssetsAndResourcesFrom(LocalResourceContainer data) {
+ return this.setAssets(data.getAssets())
+ .setResources(data.getResources())
+ .setAssetsRoots(data.getAssetRoots())
+ .setResourcesRoots(data.getResourceRoots());
+ }
+
+ public abstract Builder setLabel(Label label);
+
+ abstract Builder setJavaPackage(@Nullable String javaPackage);
+
+ public abstract Builder setRenameManifestPackage(@Nullable String renameManifestPackage);
+
+ public abstract Builder setConstantsInlined(boolean constantsInlined);
+
+ public abstract Builder setApk(@Nullable Artifact apk);
+
+ public abstract Builder setManifest(Artifact manifest);
+
+ @Nullable
+ abstract Artifact getJavaSourceJar();
+
+ public abstract Builder setJavaSourceJar(@Nullable Artifact javaSourceJar);
+
+ public abstract Builder setJavaClassJar(@Nullable Artifact javaClassJar);
+
+ public abstract Builder setAssets(ImmutableList<Artifact> assets);
+
+ public abstract Builder setResources(ImmutableList<Artifact> resources);
+
+ public abstract Builder setAssetsRoots(ImmutableList<PathFragment> assetsRoots);
+
+ public abstract Builder setResourcesRoots(ImmutableList<PathFragment> resourcesRoots);
+
+ public abstract Builder setManifestExported(boolean manifestExported);
+
+ public abstract Builder setRTxt(@Nullable Artifact rTxt);
+
+ public abstract Builder setSymbolsTxt(@Nullable Artifact symbolsTxt);
+
+ abstract ResourceContainer autoBuild();
+
+ /**
+ * Builds and returns the ResourceContainer.
+ *
+ * <p>May result in the rule context adding a rule error if the Java package was to be set from
+ * the source jar path, but the source jar does not have an acceptable Java package.
+ */
+ public ResourceContainer build() {
+ if (javaPackageSource == JavaPackageSource.SOURCE_JAR_PATH) {
+ Preconditions.checkState(
+ !(javaPackageSource == JavaPackageSource.SOURCE_JAR_PATH && ruleContext == null),
+ "setJavaPackageFrom(SOURCE_JAR_PATH) is only permitted when using builderFromRule.");
+ Preconditions.checkState(
+ getJavaSourceJar() != null,
+ "setJavaPackageFrom(SOURCE_JAR_PATH) was called, but no source jar was set.");
+ setJavaPackage(getJavaPackageFromSourceJarPath());
+ }
+ return autoBuild();
+ }
+
+ @Nullable
+ private String getJavaPackageFromSourceJarPath() {
+ if (javaPackageSource != JavaPackageSource.SOURCE_JAR_PATH) {
+ return null;
+ }
+ if (hasCustomPackage(ruleContext)) {
+ return ruleContext.attributes().get("custom_package", Type.STRING);
+ }
+ Artifact rJavaSrcJar = getJavaSourceJar();
+ // TODO(bazel-team): JavaUtil.getJavaPackageName does not check to see if the path is valid.
+ // So we need to check for the JavaRoot.
+ if (JavaUtil.getJavaRoot(rJavaSrcJar.getExecPath()) == null) {
+ ruleContext.ruleError(
+ "The location of your BUILD file determines the Java package used for "
+ + "Android resource processing. A directory named \"java\" or \"javatests\" will "
+ + "be used as your Java source root and the path of your BUILD file relative to "
+ + "the Java source root will be used as the package for Android resource "
+ + "processing. The Java source root could not be determined for \""
+ + ruleContext.getPackageDirectory()
+ + "\". Move your BUILD file under a java or javatests directory, or set the "
+ + "'custom_package' attribute.");
+ }
+ return JavaUtil.getJavaPackageName(rJavaSrcJar.getExecPath());
+ }
+
+ private static boolean hasCustomPackage(RuleContext ruleContext) {
+ return ruleContext.attributes().isAttributeValueExplicitlySpecified("custom_package");
+ }
+
+ private static boolean getExportsManifest(RuleContext ruleContext) {
+ // AndroidLibraryBaseRule has exports_manifest but AndroidBinaryBaseRule does not.
+ // ResourceContainers are built for both, so we must check if exports_manifest is present.
+ return ruleContext.attributes().has("exports_manifest", Type.BOOLEAN)
+ && ruleContext.attributes().get("exports_manifest", Type.BOOLEAN);
+ }
+
+ @Nullable
+ private static String getRenameManifestPackage(RuleContext ruleContext) {
+ return ruleContext.attributes().isAttributeValueExplicitlySpecified("rename_manifest_package")
+ ? ruleContext.attributes().get("rename_manifest_package", Type.STRING)
+ : null;
+ }
+ }
+}