diff options
Diffstat (limited to 'src/tools/android/java/com/google')
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); |