aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainer.java
diff options
context:
space:
mode:
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;
+ }
+ }
+}