aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/android/java/com/google')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java61
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/Converters.java152
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java1
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ValidateAndLinkResourcesAction.java114
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/aapt2/Aapt2ConfigOptions.java50
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/aapt2/CompiledResources.java44
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceLinker.java73
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/aapt2/StaticLibrary.java67
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java3
9 files changed, 502 insertions, 63 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java b/src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java
index 4bfe5cb10c..a0549c879b 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java
@@ -15,9 +15,14 @@ package com.google.devtools.build.android;
import com.android.builder.core.VariantType;
import com.android.repository.Revision;
+import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
+import com.google.common.io.CharStreams;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
@@ -28,6 +33,7 @@ import javax.annotation.Nullable;
* and variant type.
*/
public class AaptCommandBuilder {
+
private final ImmutableList.Builder<String> flags = new ImmutableList.Builder<>();
private Revision buildToolsVersion;
private VariantType variantType;
@@ -60,8 +66,8 @@ public class AaptCommandBuilder {
/**
* Adds a flag to the builder, along with a string value. The two will be added as different words
- * in the final command line. If the value is {@code null}, neither the flag nor the value will
- * be added.
+ * in the final command line. If the value is {@code null}, neither the flag nor the value will be
+ * added.
*/
public AaptCommandBuilder add(String flag, @Nullable String value) {
Preconditions.checkNotNull(flag);
@@ -88,13 +94,13 @@ public class AaptCommandBuilder {
}
/**
- * Adds a flag to the builder multiple times, once for each value in the given collection.
- * {@code null} values will be skipped. If the collection is empty, nothing will be added.
- * The values will be added in the source collection's iteration order.
+ * Adds a flag to the builder multiple times, once for each value in the given collection. {@code
+ * null} values will be skipped. If the collection is empty, nothing will be added. The values
+ * will be added in the source collection's iteration order.
*
- * <p>ex. If {@code flag} is {@code "-0"} and {@code values} contains the values
- * {@code "png"}, {@code null}, and {@code "gif"}, then four words will be added to the final
- * command line: {@code "-0", "png", "-0", "gif"}.
+ * <p>ex. If {@code flag} is {@code "-0"} and {@code values} contains the values {@code "png"},
+ * {@code null}, and {@code "gif"}, then four words will be added to the final command line:
+ * {@code "-0", "png", "-0", "gif"}.
*/
public AaptCommandBuilder addRepeated(String flag, Collection<String> values) {
Preconditions.checkNotNull(flag);
@@ -120,8 +126,8 @@ public class AaptCommandBuilder {
}
/**
- * Adds the next flag to the builder only if the build tools version is unspecified or is
- * greater than or equal to the given version.
+ * Adds the next flag to the builder only if the build tools version is unspecified or is greater
+ * than or equal to the given version.
*/
public ConditionalAaptCommandBuilder whenVersionIsAtLeast(Revision requiredVersion) {
Preconditions.checkNotNull(requiredVersion);
@@ -133,9 +139,7 @@ public class AaptCommandBuilder {
return flags.build();
}
- /**
- * Wrapper for potentially adding flags to an AaptCommandBuilder based on a conditional.
- */
+ /** Wrapper for potentially adding flags to an AaptCommandBuilder based on a conditional. */
public interface ConditionalAaptCommandBuilder {
/**
* Adds a single flag to the builder if the condition was true.
@@ -161,8 +165,8 @@ public class AaptCommandBuilder {
AaptCommandBuilder thenAdd(String flag, @Nullable Path value);
/**
- * Adds the values in the collection to the builder, each preceded by the given flag,
- * if the collection was non-empty and the condition was true.
+ * Adds the values in the collection to the builder, each preceded by the given flag, if the
+ * collection was non-empty and the condition was true.
*
* @see AaptCommandBuilder#addRepeated(String,Collection<String>)
*/
@@ -200,9 +204,7 @@ public class AaptCommandBuilder {
}
}
- /**
- * Null implementation of ConditionalAaptCommandBuilder returned when a condition is false.
- */
+ /** Null implementation of ConditionalAaptCommandBuilder returned when a condition is false. */
private static class FailedConditionCommandBuilder implements ConditionalAaptCommandBuilder {
private final AaptCommandBuilder originalCommandBuilder;
@@ -235,5 +237,26 @@ public class AaptCommandBuilder {
return originalCommandBuilder;
}
}
-}
+ /**
+ * Executes command and returns log.
+ *
+ * @throws IOException when the process cannot execute.
+ */
+ public String execute(String action) throws IOException {
+ StringBuilder processLog = new StringBuilder();
+ final Process process = new ProcessBuilder().command(build()).redirectErrorStream(true).start();
+ processLog.append("Command: ");
+ Joiner.on(" ").appendTo(processLog, build());
+ processLog.append("\n");
+ final InputStreamReader stdout =
+ new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8);
+ while (process.isAlive()) {
+ processLog.append(CharStreams.toString(stdout));
+ }
+ if (process.exitValue() != 0) {
+ throw new RuntimeException(String.format("Error during %s:", action) + "\n" + processLog);
+ }
+ return processLog.toString();
+ }
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/Converters.java b/src/tools/android/java/com/google/devtools/build/android/Converters.java
index f991a076c6..db88ade4d1 100644
--- a/src/tools/android/java/com/google/devtools/build/android/Converters.java
+++ b/src/tools/android/java/com/google/devtools/build/android/Converters.java
@@ -17,12 +17,17 @@ import com.android.builder.core.VariantType;
import com.android.manifmerger.ManifestMerger2;
import com.android.manifmerger.ManifestMerger2.MergeType;
import com.android.repository.Revision;
+import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
+import com.google.devtools.build.android.aapt2.CompiledResources;
+import com.google.devtools.build.android.aapt2.StaticLibrary;
import com.google.devtools.common.options.Converter;
import com.google.devtools.common.options.EnumConverter;
import com.google.devtools.common.options.OptionsParsingException;
+import java.io.File;
import java.lang.reflect.ParameterizedType;
import java.nio.file.FileSystems;
import java.nio.file.Files;
@@ -33,25 +38,30 @@ import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.annotation.Nullable;
/**
* Some convenient converters used by android actions. Note: These are specific to android actions.
*/
public final class Converters {
- private static final Converter<String> IDENTITY_CONVERTER = new Converter<String>() {
- @Override public String convert(String input) {
- return input;
- }
+ private static final Converter<String> IDENTITY_CONVERTER =
+ new Converter<String>() {
+ @Override
+ public String convert(String input) {
+ return input;
+ }
- @Override public String getTypeDescription() {
- return "a string";
- }
- };
+ @Override
+ public String getTypeDescription() {
+ return "a string";
+ }
+ };
/**
- * Converter for {@link UnvalidatedAndroidData}. Relies on
- * {@code UnvalidatedAndroidData#valueOf(String)} to perform conversion and validation.
+ * Converter for {@link UnvalidatedAndroidData}. Relies on {@code
+ * UnvalidatedAndroidData#valueOf(String)} to perform conversion and validation.
*/
public static class UnvalidatedAndroidDataConverter implements Converter<UnvalidatedAndroidData> {
@@ -71,9 +81,7 @@ public final class Converters {
}
}
- /**
- * Converter for {@link UnvalidatedAndroidDirectories}.
- */
+ /** Converter for {@link UnvalidatedAndroidDirectories}. */
public static class UnvalidatedAndroidDirectoriesConverter
implements Converter<UnvalidatedAndroidDirectories> {
@@ -95,8 +103,8 @@ public final class Converters {
}
/**
- * Converter for a list of {@link DependencyAndroidData}. Relies on
- * {@code DependencyAndroidData#valueOf(String)} to perform conversion and validation.
+ * Converter for a list of {@link DependencyAndroidData}. Relies on {@code
+ * DependencyAndroidData#valueOf(String)} to perform conversion and validation.
*/
public static class DependencyAndroidDataListConverter
implements Converter<List<DependencyAndroidData>> {
@@ -121,13 +129,12 @@ public final class Converters {
@Override
public String getTypeDescription() {
return "a list of dependency android data in the format "
- + DependencyAndroidData.EXPECTED_FORMAT + "[,...]";
+ + DependencyAndroidData.EXPECTED_FORMAT
+ + "[,...]";
}
}
- /**
- * Converter for a {@link SerializedAndroidData}.
- */
+ /** Converter for a {@link SerializedAndroidData}. */
public static class SerializedAndroidDataConverter implements Converter<SerializedAndroidData> {
@Override
@@ -146,9 +153,7 @@ public final class Converters {
}
}
- /**
- * Converter for a list of {@link SerializedAndroidData}.
- */
+ /** Converter for a list of {@link SerializedAndroidData}. */
public static class SerializedAndroidDataListConverter
implements Converter<List<SerializedAndroidData>> {
@@ -172,7 +177,8 @@ public final class Converters {
@Override
public String getTypeDescription() {
return "a list of preparsed android data in the format "
- + SerializedAndroidData.EXPECTED_FORMAT + "[&...]";
+ + SerializedAndroidData.EXPECTED_FORMAT
+ + "[&...]";
}
}
@@ -200,8 +206,8 @@ public final class Converters {
}
/**
- * Converter for a list of {@link DependencySymbolFileProvider}. Relies on
- * {@code DependencySymbolFileProvider#valueOf(String)} to perform conversion and validation.
+ * Converter for a list of {@link DependencySymbolFileProvider}. Relies on {@code
+ * DependencySymbolFileProvider#valueOf(String)} to perform conversion and validation.
*
* @deprecated use multi-value flags and {@link DependencySymbolFileProviderConverter} instead.
*/
@@ -228,15 +234,16 @@ public final class Converters {
@Override
public String getTypeDescription() {
- return String.format("a list of dependency android data in the format: %s[%s]",
+ return String.format(
+ "a list of dependency android data in the format: %s[%s]",
DependencySymbolFileProvider.commandlineFormat("1"),
DependencySymbolFileProvider.commandlineFormat("2"));
}
}
/**
- * Converter for {@link Revision}. Relies on {@code Revision#parseRevision(String)} to
- * perform conversion and validation.
+ * Converter for {@link Revision}. Relies on {@code Revision#parseRevision(String)} to perform
+ * conversion and validation.
*/
public static class RevisionConverter implements Converter<Revision> {
@@ -306,8 +313,7 @@ public final class Converters {
}
/** Converter for {@link ManifestMerger2}.{@link MergeType}. */
- public static class MergeTypeConverter
- extends EnumConverter<MergeType> {
+ public static class MergeTypeConverter extends EnumConverter<MergeType> {
public MergeTypeConverter() {
super(MergeType.class, "merge type");
}
@@ -324,8 +330,7 @@ public final class Converters {
}
/**
- * Validating converter for a list of Paths.
- * A Path is considered valid if it resolves to a file.
+ * Validating converter for a list of Paths. A Path is considered valid if it resolves to a file.
*/
@Deprecated
public static class PathListConverter implements Converter<List<Path>> {
@@ -389,21 +394,19 @@ public final class Converters {
for (String entry : input.split(UNESCAPED_COMMA_REGEX)) {
String[] entryFields = entry.split(UNESCAPED_COLON_REGEX, -1);
if (entryFields.length < 2) {
- throw new OptionsParsingException(String.format(
- "Dictionary entry [%s] does not contain both a key and a value.",
- entry));
+ throw new OptionsParsingException(
+ String.format(
+ "Dictionary entry [%s] does not contain both a key and a value.", entry));
} else if (entryFields.length > 2) {
- throw new OptionsParsingException(String.format(
- "Dictionary entry [%s] contains too many fields.",
- entry));
+ throw new OptionsParsingException(
+ String.format("Dictionary entry [%s] contains too many fields.", entry));
}
// Unescape any comma or colon that is not a key or value separator.
String keyString = unescapeInput(entryFields[0]);
K key = keyConverter.convert(keyString);
if (map.containsKey(key)) {
- throw new OptionsParsingException(String.format(
- "Dictionary already contains the key [%s].",
- keyString));
+ throw new OptionsParsingException(
+ String.format("Dictionary already contains the key [%s].", keyString));
}
// Unescape any comma or colon that is not a key or value separator.
String valueString = unescapeInput(entryFields[1]);
@@ -435,7 +438,8 @@ public final class Converters {
}
// The way {@link OptionsData} checks for generic types requires convert to have literal type
// parameters and not argument type parameters.
- @Override public Map<String, String> convert(String input) throws OptionsParsingException {
+ @Override
+ public Map<String, String> convert(String input) throws OptionsParsingException {
return super.convert(input);
}
}
@@ -452,8 +456,70 @@ public final class Converters {
}
// The way {@link OptionsData} checks for generic types requires convert to have literal type
// parameters and not argument type parameters.
- @Override public Map<Path, String> convert(String input) throws OptionsParsingException {
+ @Override
+ public Map<Path, String> convert(String input) throws OptionsParsingException {
return super.convert(input);
}
}
+
+ /** Converts a list of static library strings into paths. */
+ @Deprecated
+ public static class StaticLibraryListConverter implements Converter<List<StaticLibrary>> {
+ static final Splitter SPLITTER = Splitter.on(File.pathSeparator);
+
+ static final StaticLibraryConverter libraryConverter = new StaticLibraryConverter();
+
+ @Override
+ public List<StaticLibrary> convert(String input) throws OptionsParsingException {
+ final Builder<StaticLibrary> builder = ImmutableList.<StaticLibrary>builder();
+ for (String unused : SPLITTER.splitToList(input)) {
+ builder.add(libraryConverter.convert(input));
+ }
+ return builder.build();
+ }
+
+ @Override
+ public String getTypeDescription() {
+ return "Static resource libraries.";
+ }
+ }
+
+ /** Converts a static library string into path. */
+ public static class StaticLibraryConverter implements Converter<StaticLibrary> {
+ static final Splitter SPLITTER = Splitter.on(File.pathSeparator);
+
+ static final PathConverter pathConverter = new PathConverter(true);
+
+ @Override
+ public StaticLibrary convert(String input) throws OptionsParsingException {
+ return StaticLibrary.from(pathConverter.convert(input));
+ }
+
+ @Override
+ public String getTypeDescription() {
+ return "Static resource library.";
+ }
+ }
+
+ /** Converts a string of resources and manifest into paths. */
+ public static class CompiledResourcesConverter implements Converter<CompiledResources> {
+ static final PathConverter pathConverter = new PathConverter(true);
+ static final Pattern COMPILED_RESOURCE_FORMAT = Pattern.compile("(.+):(.+)");
+
+ @Override
+ public CompiledResources convert(String input) throws OptionsParsingException {
+ final Matcher matched = COMPILED_RESOURCE_FORMAT.matcher(input);
+ if (!matched.find()) {
+ throw new OptionsParsingException("Expected format <resources zip>:<manifest>");
+ }
+ Path resources = pathConverter.convert(matched.group(1));
+ Path manifest = pathConverter.convert(matched.group(2));
+ return CompiledResources.from(resources, manifest);
+ }
+
+ @Override
+ public String getTypeDescription() {
+ return "Compiled resources zip.";
+ }
+ }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java b/src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java
index 58e499589a..9d0575e2e8 100644
--- a/src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java
+++ b/src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java
@@ -558,6 +558,7 @@ public class FullyQualifiedName implements DataKey {
if (!(obj instanceof FullyQualifiedName)) {
return false;
}
+
FullyQualifiedName other = getClass().cast(obj);
return Objects.equals(pkg, other.pkg)
&& Objects.equals(type, other.type)
diff --git a/src/tools/android/java/com/google/devtools/build/android/ValidateAndLinkResourcesAction.java b/src/tools/android/java/com/google/devtools/build/android/ValidateAndLinkResourcesAction.java
new file mode 100644
index 0000000000..bca1bc9a63
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/ValidateAndLinkResourcesAction.java
@@ -0,0 +1,114 @@
+// 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.
+// Copyright 2017 The Bazel Authors. All rights reserved.
+package com.google.devtools.build.android;
+
+import com.google.devtools.build.android.aapt2.Aapt2ConfigOptions;
+import com.google.devtools.build.android.aapt2.CompiledResources;
+import com.google.devtools.build.android.aapt2.ResourceLinker;
+import com.google.devtools.build.android.aapt2.StaticLibrary;
+import com.google.devtools.common.options.Option;
+import com.google.devtools.common.options.OptionDocumentationCategory;
+import com.google.devtools.common.options.OptionEffectTag;
+import com.google.devtools.common.options.OptionsBase;
+import com.google.devtools.common.options.OptionsParser;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+
+/** Performs resource validation and static linking for compiled android resources. */
+public class ValidateAndLinkResourcesAction {
+
+ /** Action configuration options. */
+ public static class Options extends OptionsBase {
+ @Option(
+ name = "resources",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ defaultValue = "null",
+ converter = Converters.CompiledResourcesConverter.class,
+ category = "input",
+ help = "Compiled resources to link."
+ )
+ public CompiledResources resources;
+
+ // TODO(b/64570523): remove this flag when it is no longer used.
+ @Option(
+ name = "libraries",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ defaultValue = "null",
+ converter = Converters.StaticLibraryListConverter.class,
+ category = "input",
+ help = "Static libraries to link against. Deprecated, use --library"
+ )
+ public List<StaticLibrary> deprecatedLibraries;
+
+ @Option(
+ name = "library",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ defaultValue = "null",
+ converter = Converters.StaticLibraryConverter.class,
+ category = "input",
+ allowMultiple = true,
+ help = "Static libraries to link against."
+ )
+ public List<StaticLibrary> libraries;
+
+ @Option(
+ name = "staticLibraryOut",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ defaultValue = "null",
+ converter = Converters.PathConverter.class,
+ category = "output",
+ help = "Static library produced."
+ )
+ public Path staticLibraryOut;
+
+ @Option(
+ name = "rTxtOut",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ defaultValue = "null",
+ converter = Converters.PathConverter.class,
+ category = "output",
+ help = "R.txt out."
+ )
+ public Path rTxtOut;
+ }
+
+ public static void main(String[] args) throws Exception {
+ final OptionsParser optionsParser =
+ OptionsParser.newOptionsParser(Options.class, Aapt2ConfigOptions.class);
+ optionsParser.enableParamsFileSupport(FileSystems.getDefault());
+ optionsParser.parse(args);
+
+ Options options = optionsParser.getOptions(Options.class);
+ final Aapt2ConfigOptions aapt2Options = optionsParser.getOptions(Aapt2ConfigOptions.class);
+
+ try (ScopedTemporaryDirectory scopedTmp =
+ new ScopedTemporaryDirectory("android_resources_tmp")) {
+
+ ResourceLinker.create(aapt2Options.aapt2, scopedTmp.getPath())
+ .dependencies(Optional.ofNullable(options.deprecatedLibraries).orElse(options.libraries))
+ .buildVersion(aapt2Options.buildToolsVersion)
+ .linkStatically(options.resources)
+ .copyLibraryTo(options.staticLibraryOut)
+ .copyRTxtTo(options.rTxtOut);
+ }
+ }
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/aapt2/Aapt2ConfigOptions.java b/src/tools/android/java/com/google/devtools/build/android/aapt2/Aapt2ConfigOptions.java
new file mode 100644
index 0000000000..bfe57af3a1
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/aapt2/Aapt2ConfigOptions.java
@@ -0,0 +1,50 @@
+// 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.
+// Copyright 2017 The Bazel Authors. All rights reserved.
+
+package com.google.devtools.build.android.aapt2;
+
+import com.android.repository.Revision;
+import com.google.devtools.build.android.Converters.ExistingPathConverter;
+import com.google.devtools.build.android.Converters.RevisionConverter;
+import com.google.devtools.common.options.Option;
+import com.google.devtools.common.options.OptionDocumentationCategory;
+import com.google.devtools.common.options.OptionEffectTag;
+import com.google.devtools.common.options.OptionsBase;
+import java.nio.file.Path;
+
+/** Aaprt2 specific configuration options. */
+public class Aapt2ConfigOptions extends OptionsBase {
+ @Option(
+ name = "aapt2",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ defaultValue = "null",
+ converter = ExistingPathConverter.class,
+ category = "tool",
+ help = "Aapt2 tool location for resource compilation."
+ )
+ public Path aapt2;
+
+ @Option(
+ name = "buildToolsVersion",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ defaultValue = "null",
+ converter = RevisionConverter.class,
+ category = "config",
+ help = "Version of the build tools (e.g. aapt) being used, e.g. 23.0.2"
+ )
+ public Revision buildToolsVersion;
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/aapt2/CompiledResources.java b/src/tools/android/java/com/google/devtools/build/android/aapt2/CompiledResources.java
new file mode 100644
index 0000000000..65f9c1a9a6
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/aapt2/CompiledResources.java
@@ -0,0 +1,44 @@
+// 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.android.aapt2;
+
+import java.nio.file.Path;
+
+/**
+ * Contains reference to the aapt2 generated .flat file archive and a manifest.
+ *
+ * <p>This represents the state between the aapt2 compile and link actions.
+ */
+public class CompiledResources {
+
+ private final Path resources;
+ private final Path manifest;
+
+ private CompiledResources(Path resources, Path manifest) {
+ this.resources = resources;
+ this.manifest = manifest;
+ }
+
+ public static CompiledResources from(Path resources, Path manifest) {
+ return new CompiledResources(resources, manifest);
+ }
+
+ public Path asZip() {
+ return resources;
+ }
+
+ public Path asManifest() {
+ return manifest;
+ }
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceLinker.java b/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceLinker.java
new file mode 100644
index 0000000000..eb17f2dc9f
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceLinker.java
@@ -0,0 +1,73 @@
+// 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.android.aapt2;
+
+import com.android.builder.core.VariantType;
+import com.android.repository.Revision;
+import com.google.devtools.build.android.AaptCommandBuilder;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+
+/** Performs linking of {@link CompiledResources} using aapt2. */
+public class ResourceLinker {
+
+ private final Path aapt2;
+ private List<StaticLibrary> libraries;
+ private Revision buildToolsVersion;
+ private final Path workingDirectory;
+
+ private ResourceLinker(Path aapt2, Path workingDirectory) {
+ this.aapt2 = aapt2;
+ this.workingDirectory = workingDirectory;
+ }
+
+ public static ResourceLinker create(Path aapt2, Path workingDirectory) {
+ return new ResourceLinker(aapt2, workingDirectory);
+ }
+
+ /** Dependent static libraries to be linked to. */
+ public ResourceLinker dependencies(List<StaticLibrary> libraries) {
+ this.libraries = libraries;
+ return this;
+ }
+
+ public ResourceLinker buildVersion(Revision buildToolsVersion) {
+ this.buildToolsVersion = buildToolsVersion;
+ return this;
+ }
+
+ /**
+ * Statically links the {@link CompiledResources} with the dependencies to produce a {@link
+ * StaticLibrary}.
+ *
+ * @throws IOException
+ */
+ public StaticLibrary linkStatically(CompiledResources resources) throws IOException {
+ final Path outPath = workingDirectory.resolve("lib.ap_");
+ new AaptCommandBuilder(aapt2)
+ .forBuildToolsVersion(buildToolsVersion)
+ .forVariantType(VariantType.LIBRARY)
+ .add("link")
+ .add("--manifest", resources.asManifest())
+ .add("--static-lib")
+ .add("--output-text-symbols", workingDirectory)
+ .add("-o", outPath)
+ .add("--auto-add-overlay")
+ .addRepeated("-I", StaticLibrary.toPathStrings(libraries))
+ .add("-R", resources.asZip())
+ .execute(String.format("Linking %s", resources));
+ return StaticLibrary.from(outPath);
+ }
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/aapt2/StaticLibrary.java b/src/tools/android/java/com/google/devtools/build/android/aapt2/StaticLibrary.java
new file mode 100644
index 0000000000..8828e87042
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/aapt2/StaticLibrary.java
@@ -0,0 +1,67 @@
+// 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.android.aapt2;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+/** A library generated by aapt2. */
+public class StaticLibrary {
+
+ private final Path library;
+ private final Optional<Path> rTxt;
+
+ private static final Function<StaticLibrary, String> LIBRARY_TO_STRING =
+ input -> input.library.toString();
+
+ private StaticLibrary(Path library, Optional<Path> rTxt) {
+ this.library = library;
+ this.rTxt = rTxt;
+ }
+
+ public static StaticLibrary from(Path library) {
+ return new StaticLibrary(library, Optional.empty());
+ }
+
+ public static StaticLibrary from(Path library, Path rTxt) {
+ return new StaticLibrary(library, Optional.of(rTxt));
+ }
+
+ public StaticLibrary copyLibraryTo(Path target) throws IOException {
+ return new StaticLibrary(Files.copy(library, target), rTxt);
+ }
+
+ public StaticLibrary copyRTxtTo(final Path target) {
+ return new StaticLibrary(
+ library,
+ rTxt.map(
+ input -> {
+ try {
+ return Files.copy(input, target);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }));
+ }
+
+ public static Collection<String> toPathStrings(List<StaticLibrary> libraries) {
+ return Lists.transform(libraries, LIBRARY_TO_STRING);
+ }
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java
index 6be244b115..661e5ddf98 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java
@@ -364,8 +364,9 @@ public class AttrXmlResourceValue implements XmlResourceValue {
throw new IllegalArgumentException(this + " is not a combinable resource.");
}
+ /** Represents the xml value for an attr definition. */
@CheckReturnValue
- interface ResourceXmlAttrValue {
+ public interface ResourceXmlAttrValue {
ValuesResourceDefinition writeTo(ValuesResourceDefinition writer);