aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/test/java/com/google/devtools/build/android/DependencyAndroidDataTest.java2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java20
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java8
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidDataDeserializer.java2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidManifestProcessor.java37
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java11
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/CompileLibraryResourcesAction.java91
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java19
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DeserializationException.java5
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ExecutorServiceCloser.java3
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidData.java41
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidDirectories.java3
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ValidateAndLinkResourcesAction.java68
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/aapt2/Aapt2ConfigOptions.java99
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/aapt2/CompiledResources.java12
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceCompiler.java44
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceLinker.java29
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/aapt2/StaticLibrary.java79
18 files changed, 417 insertions, 156 deletions
diff --git a/src/test/java/com/google/devtools/build/android/DependencyAndroidDataTest.java b/src/test/java/com/google/devtools/build/android/DependencyAndroidDataTest.java
index 2a83860903..8fcbbc073f 100644
--- a/src/test/java/com/google/devtools/build/android/DependencyAndroidDataTest.java
+++ b/src/test/java/com/google/devtools/build/android/DependencyAndroidDataTest.java
@@ -59,7 +59,7 @@ public class DependencyAndroidDataTest {
@Test public void flagFullParse() throws Exception{
Truth.assertThat(
DependencyAndroidData.valueOf(
- "res#otherres:assets#otherassets:AndroidManifest.xml:r.txt:symbols.bin:static.library.ap_",
+ "res#otherres:assets#otherassets:AndroidManifest.xml:r.txt:static.library.ap_:symbols.bin",
fileSystem))
.isEqualTo(
new DependencyAndroidData(
diff --git a/src/tools/android/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java b/src/tools/android/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java
index 5e168414c9..d975051497 100644
--- a/src/tools/android/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java
@@ -19,6 +19,7 @@ import static java.util.stream.Collectors.toList;
import com.android.utils.StdLogger;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.devtools.build.android.AndroidResourceMerger.MergingException;
import com.google.devtools.build.android.AndroidResourceProcessingAction.Options;
@@ -86,13 +87,29 @@ public class Aapt2ResourcePackagingAction {
final Path densityManifest = tmp.resolve("manifest-filtered/AndroidManifest.xml");
final Path processedManifest = tmp.resolve("manifest-processed/AndroidManifest.xml");
+ final Path dummyManifest = tmp.resolve("manifest-aapt-dummy/AndroidManifest.xml");
final Path databindingResourcesRoot =
Files.createDirectories(tmp.resolve("android_data_binding_resources"));
+ final Path databindingMetaData =
+ Files.createDirectories(tmp.resolve("android_data_binding_metadata"));
final Path compiledResources = Files.createDirectories(tmp.resolve("compiled"));
+ final Path staticLinkedOut = Files.createDirectories(tmp.resolve("static-linked"));
final Path linkedOut = Files.createDirectories(tmp.resolve("linked"));
+ Path generatedSources = null;
+ if (options.srcJarOutput != null || options.rOutput != null || options.symbolsOut != null) {
+ generatedSources = tmp.resolve("generated_resources");
+ }
+
logger.fine(String.format("Setup finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
+ List<DependencyAndroidData> data =
+ ImmutableSet.<DependencyAndroidData>builder()
+ .addAll(options.directData)
+ .addAll(options.transitiveData)
+ .build()
+ .asList();
+
// Checks for merge conflicts.
MergedAndroidData mergedAndroidData =
AndroidResourceMerger.mergeData(
@@ -125,7 +142,8 @@ public class Aapt2ResourcePackagingAction {
CompiledResources compiled =
options
.primaryData
- .processDataBindings(options.dataBindingInfoOut, databindingResourcesRoot)
+ .processDataBindings(options.dataBindingInfoOut, options.packageForR,
+ databindingResourcesRoot)
.compile(compiler, compiledResources)
.processManifest(
manifest ->
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 a0549c879b..6e9c9a41ab 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
@@ -244,10 +244,12 @@ public class AaptCommandBuilder {
* @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();
+ final StringBuilder processLog = new StringBuilder();
+ List<String> command = build();
+
+ final Process process = new ProcessBuilder().command(command).redirectErrorStream(true).start();
processLog.append("Command: ");
- Joiner.on(" ").appendTo(processLog, build());
+ Joiner.on("\n\t").appendTo(processLog, command);
processLog.append("\n");
final InputStreamReader stdout =
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8);
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidDataDeserializer.java b/src/tools/android/java/com/google/devtools/build/android/AndroidDataDeserializer.java
index f48a7966c6..1f9d50090c 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidDataDeserializer.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataDeserializer.java
@@ -112,7 +112,7 @@ public class AndroidDataDeserializer {
}
readEntriesSegment(consumers, in, currentFileSystem, header);
} catch (IOException e) {
- throw new DeserializationException(e);
+ throw new DeserializationException("Error deserializing " + inPath, e);
} finally {
logger.fine(
String.format("Deserialized in merged in %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidManifestProcessor.java b/src/tools/android/java/com/google/devtools/build/android/AndroidManifestProcessor.java
index be6738fa3b..cd9e6f8654 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidManifestProcessor.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidManifestProcessor.java
@@ -246,13 +246,29 @@ public class AndroidManifestProcessor {
Path manifest,
Path processedManifest) {
- ManifestMerger2.MergeType mergeType = ManifestMerger2.MergeType.APPLICATION;
+ if (versionCode != -1 || versionName != null || applicationId != null) {
+ processManifest(
+ versionCode, versionName, manifest, processedManifest, MergeType.APPLICATION,
+ applicationId);
+ return processedManifest;
+ }
+ return manifest;
+ }
- String newManifestPackage = applicationId;
+ /** Processes the manifest for a library and return the manifest Path. */
+ public Path processLibraryManifest(
+ String newManifestPackage,
+ Path manifest,
+ Path processedManifest) {
- if (versionCode != -1 || versionName != null || newManifestPackage != null) {
+ if (newManifestPackage != null) {
processManifest(
- versionCode, versionName, manifest, processedManifest, mergeType, newManifestPackage);
+ -1 /* versionCode */,
+ null /* versionName */,
+ manifest,
+ processedManifest,
+ MergeType.LIBRARY,
+ newManifestPackage);
return processedManifest;
}
return manifest;
@@ -377,4 +393,17 @@ public class AndroidManifestProcessor {
throw new ManifestProcessingException(e);
}
}
+
+ public static Path writeDummyManifestForAapt(Path dummyManifest, String packageForR) {
+ try {
+ Files.createDirectories(dummyManifest.getParent());
+ return Files.write(dummyManifest, String.format(
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\""
+ + " package=\"%s\">"
+ + "</manifest>", packageForR).getBytes(UTF_8));
+ } catch (IOException e) {
+ throw new ManifestProcessingException(e);
+ }
+ }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java
index 58ca71bd4f..6c8b86b3e1 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java
@@ -13,8 +13,6 @@
// limitations under the License.
package com.google.devtools.build.android;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
import android.databinding.AndroidDataBinding;
import android.databinding.cli.ProcessXmlOptions;
import com.android.annotations.NonNull;
@@ -689,13 +687,8 @@ public class AndroidResourceProcessor {
}
}
- public void writeDummyManifestForAapt(Path dummyManifest, String packageForR) throws IOException {
- Files.createDirectories(dummyManifest.getParent());
- Files.write(dummyManifest, String.format(
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
- + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\""
- + " package=\"%s\">"
- + "</manifest>", packageForR).getBytes(UTF_8));
+ public static void writeDummyManifestForAapt(Path dummyManifest, String packageForR) {
+ AndroidManifestProcessor.writeDummyManifestForAapt(dummyManifest, packageForR);
}
/**
diff --git a/src/tools/android/java/com/google/devtools/build/android/CompileLibraryResourcesAction.java b/src/tools/android/java/com/google/devtools/build/android/CompileLibraryResourcesAction.java
index a6afad5ad0..7c19745a1c 100644
--- a/src/tools/android/java/com/google/devtools/build/android/CompileLibraryResourcesAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/CompileLibraryResourcesAction.java
@@ -14,14 +14,13 @@
package com.google.devtools.build.android;
-import com.android.builder.core.VariantType;
import com.android.repository.Revision;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
import com.google.devtools.build.android.Converters.ExistingPathConverter;
import com.google.devtools.build.android.Converters.PathConverter;
import com.google.devtools.build.android.Converters.RevisionConverter;
+import com.google.devtools.build.android.Converters.UnvalidatedAndroidDirectoriesConverter;
import com.google.devtools.build.android.aapt2.ResourceCompiler;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionDocumentationCategory;
@@ -29,13 +28,9 @@ import com.google.devtools.common.options.OptionEffectTag;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;
import java.io.Closeable;
-import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executors;
import java.util.logging.Logger;
/** Compiles resources using aapt2 and archives them to zip. */
@@ -46,14 +41,13 @@ public class CompileLibraryResourcesAction {
@Option(
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
- name = "resource",
- defaultValue = "",
- allowMultiple = true,
- converter = ExistingPathConverter.class,
+ name = "resources",
+ defaultValue = "null",
+ converter = UnvalidatedAndroidDirectoriesConverter.class,
category = "input",
help = "The resources to compile with aapt2."
)
- public List<Path> resources;
+ public UnvalidatedAndroidDirectories resources;
@Option(
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
@@ -140,72 +134,25 @@ public class CompileLibraryResourcesAction {
Preconditions.checkNotNull(options.output);
Preconditions.checkNotNull(options.aapt2);
- try (ScopedTemporaryDirectory scopedTmp =
- new ScopedTemporaryDirectory("android_resources_tmp")) {
+ final ListeningExecutorService defaultService = ExecutorServiceCloser.createDefaultService();
+ try (Closeable serviceCloser = ExecutorServiceCloser.createWith(defaultService);
+ ScopedTemporaryDirectory scopedTmp =
+ new ScopedTemporaryDirectory("android_resources_tmp")) {
final Path tmp = scopedTmp.getPath();
final Path databindingResourcesRoot =
Files.createDirectories(tmp.resolve("android_data_binding_resources"));
- final Path databindingMetaData =
- Files.createDirectories(tmp.resolve("android_data_binding_metadata"));
final Path compiledResources = Files.createDirectories(tmp.resolve("compiled"));
- // The reported availableProcessors may be higher than the actual resources
- // (on a shared system). On the other hand, a lot of the work is I/O, so it's not completely
- // CPU bound. As a compromise, divide by 2 the reported availableProcessors.
- int numThreads = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
- final ListeningExecutorService executorService =
- MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(numThreads));
- try (final Closeable closeable = ExecutorServiceCloser.createWith(executorService)) {
- final ResourceCompiler compiler =
- ResourceCompiler.create(
- executorService, compiledResources, options.aapt2, options.buildToolsVersion);
- for (final Path resource :
- maybeProcessDataBindings(
- databindingResourcesRoot,
- databindingMetaData,
- options.dataBindingInfoOut,
- options.manifest,
- options.packagePath,
- options.resources)) {
- compiler.queueDirectoryForCompilation(resource);
- }
- AndroidResourceOutputs.archiveCompiledResources(
- options.output,
- databindingResourcesRoot,
- compiledResources,
- compiler.getCompiledArtifacts());
- }
- }
- }
- private static List<Path> maybeProcessDataBindings(
- Path resourceRoot,
- Path databindingMetaData,
- Path dataBindingInfoOut,
- Path manifest,
- String packagePath,
- List<Path> resources)
- throws IOException {
- if (dataBindingInfoOut == null) {
- return resources;
+ final ResourceCompiler compiler =
+ ResourceCompiler.create(
+ defaultService, compiledResources, options.aapt2, options.buildToolsVersion);
+ options
+ .resources
+ .toData(options.manifest)
+ .processDataBindings(
+ options.dataBindingInfoOut, options.packagePath, databindingResourcesRoot)
+ .compile(compiler, compiledResources)
+ .copyResourcesZipTo(options.output);
}
-
- Preconditions.checkNotNull(manifest);
- Preconditions.checkNotNull(packagePath);
-
- List<Path> processed = new ArrayList<>();
- for (Path resource : resources) {
- processed.add(
- AndroidResourceProcessor.processDataBindings(
- resourceRoot,
- resource,
- databindingMetaData,
- VariantType.LIBRARY,
- packagePath,
- manifest,
- false));
- }
-
- AndroidResourceOutputs.archiveDirectory(databindingMetaData, dataBindingInfoOut);
- return processed;
}
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java b/src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java
index f7ed888690..72f0ab5db6 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java
@@ -38,7 +38,7 @@ class DependencyAndroidData extends SerializedAndroidData {
private static final Pattern VALID_REGEX = Pattern.compile(".*:.*:.+:.+(:.*){0,2}");
public static final String EXPECTED_FORMAT =
- "resources[#resources]:assets[#assets]:manifest:r.txt:symbols.bin:static.library.ap_";
+ "resources[#resources]:assets[#assets]:manifest:r.txt:static.library.ap_:symbols.bin";
public static DependencyAndroidData valueOf(String text) {
return valueOf(text, FileSystems.getDefault());
@@ -55,15 +55,24 @@ class DependencyAndroidData extends SerializedAndroidData {
Path rTxt = exists(fileSystem.getPath(parts[3]));
ImmutableList<Path> assetDirs =
parts[1].length() == 0 ? ImmutableList.<Path>of() : splitPaths(parts[1], fileSystem);
+ StaticLibrary staticLibrary = null;
+ Path symbolsBin = null;
+
+ if (parts.length == 6) { // contains symbols bin and static library
+ staticLibrary = StaticLibrary.from(exists(fileSystem.getPath(parts[4])), rTxt, assetDirs);
+ symbolsBin = exists(fileSystem.getPath(parts[5]));
+ } else if (parts.length == 5) { // contains symbols bin
+ symbolsBin = exists(fileSystem.getPath(parts[4]));
+ }
+
+
return new DependencyAndroidData(
splitPaths(parts[0], fileSystem),
assetDirs,
exists(fileSystem.getPath(parts[2])),
rTxt,
- parts.length > 4 ? fileSystem.getPath(parts[4]) : null,
- parts.length > 5
- ? StaticLibrary.from(exists(fileSystem.getPath(parts[5])), rTxt, assetDirs)
- : null);
+ symbolsBin,
+ staticLibrary);
}
private final Path manifest;
diff --git a/src/tools/android/java/com/google/devtools/build/android/DeserializationException.java b/src/tools/android/java/com/google/devtools/build/android/DeserializationException.java
index ba001b33f4..34bc707062 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DeserializationException.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DeserializationException.java
@@ -35,6 +35,11 @@ public class DeserializationException extends RuntimeException {
this.isLegacy = false;
}
+ public DeserializationException(String message, IOException e) {
+ super(message, e);
+ this.isLegacy = false;
+ }
+
public boolean isLegacy() {
return isLegacy;
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/ExecutorServiceCloser.java b/src/tools/android/java/com/google/devtools/build/android/ExecutorServiceCloser.java
index cc99342ddf..24d7739782 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ExecutorServiceCloser.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ExecutorServiceCloser.java
@@ -32,7 +32,8 @@ final class ExecutorServiceCloser implements Closeable {
public void close() throws IOException {
List<Runnable> unfinishedTasks = executorService.shutdownNow();
if (!unfinishedTasks.isEmpty()) {
- throw new IOException("Shutting down the executor with unfinished tasks:" + unfinishedTasks);
+ throw new IOException(
+ "Shutting down the executor with unfinished tasks:" + unfinishedTasks.size());
}
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidData.java b/src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidData.java
index 74976543e9..736c9d4ce7 100644
--- a/src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidData.java
+++ b/src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidData.java
@@ -13,7 +13,9 @@
// limitations under the License.
package com.google.devtools.build.android;
+import com.android.builder.core.VariantType;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.android.aapt2.CompiledResources;
import com.google.devtools.build.android.aapt2.ResourceCompiler;
@@ -21,10 +23,12 @@ import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern;
+import javax.annotation.Nullable;
/**
* Android data that has yet to be merged and validated, the primary data for the Processor.
@@ -45,8 +49,7 @@ class UnvalidatedAndroidData extends UnvalidatedAndroidDirectories {
@VisibleForTesting
static UnvalidatedAndroidData valueOf(String text, FileSystem fileSystem) {
if (!VALID_REGEX.matcher(text).find()) {
- throw new IllegalArgumentException(
- text + " is not in the format '" + EXPECTED_FORMAT + "'");
+ throw new IllegalArgumentException(text + " is not in the format '" + EXPECTED_FORMAT + "'");
}
String[] parts = text.split(":");
return new UnvalidatedAndroidData(
@@ -111,16 +114,44 @@ class UnvalidatedAndroidData extends UnvalidatedAndroidDirectories {
assetDirs);
}
+ /* Processes the resources for databinding annotations if dataBindingOut is defined. */
public UnvalidatedAndroidData processDataBindings(
- Path dataBindingInfoOut, Path dataBindingWorkingDirectory) {
+ @Nullable Path dataBindingOut, String packagePath, Path dataBindingWorkingDirectory)
+ throws IOException {
- return new UnvalidatedAndroidData(resourceDirs, assetDirs, manifest) {
+ if (dataBindingOut == null) {
+ return this;
+ }
+
+ Preconditions.checkNotNull(manifest);
+ Preconditions.checkNotNull(packagePath);
+
+ final List<Path> processed = new ArrayList<>();
+ final Path metadataWorkingDirectory = dataBindingWorkingDirectory.resolve("metadata");
+ final Path databindingResourceRoot = dataBindingWorkingDirectory.resolve("resources");
+ for (Path resource : resourceDirs) {
+ processed.add(
+ AndroidResourceProcessor.processDataBindings(
+ databindingResourceRoot,
+ resource,
+ metadataWorkingDirectory,
+ VariantType.LIBRARY,
+ packagePath,
+ manifest,
+ false));
+ }
+
+ AndroidResourceOutputs.archiveDirectory(
+ metadataWorkingDirectory, dataBindingOut);
+
+ return new UnvalidatedAndroidData(ImmutableList.copyOf(processed), assetDirs, manifest) {
@Override
protected CompiledResources archiveCompiledResources(
List<Path> resources, Path workingDirectory, Path output) throws IOException {
+ // Update the archiving to ensure that the resources are correctly placed.
return CompiledResources.from(
AndroidResourceOutputs.archiveCompiledResources(
- output, dataBindingWorkingDirectory, workingDirectory, resources),
+ output, databindingResourceRoot, workingDirectory, resources),
manifest,
assetDirs);
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidDirectories.java b/src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidDirectories.java
index 5723f6ec61..8be10be0fb 100644
--- a/src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidDirectories.java
+++ b/src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidDirectories.java
@@ -107,4 +107,7 @@ public class UnvalidatedAndroidDirectories {
&& Objects.equals(other.assetDirs, assetDirs);
}
+ public UnvalidatedAndroidData toData(Path manifest) {
+ return new UnvalidatedAndroidData(resourceDirs, assetDirs, manifest);
+ }
}
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
index bca1bc9a63..9886be2778 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ValidateAndLinkResourcesAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ValidateAndLinkResourcesAction.java
@@ -14,6 +14,7 @@
// Copyright 2017 The Bazel Authors. All rights reserved.
package com.google.devtools.build.android;
+import com.google.common.base.Preconditions;
import com.google.devtools.build.android.aapt2.Aapt2ConfigOptions;
import com.google.devtools.build.android.aapt2.CompiledResources;
import com.google.devtools.build.android.aapt2.ResourceLinker;
@@ -34,6 +35,34 @@ public class ValidateAndLinkResourcesAction {
/** Action configuration options. */
public static class Options extends OptionsBase {
@Option(
+ name = "compiled",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ defaultValue = "null",
+ converter = Converters.ExistingPathConverter.class,
+ category = "input",
+ help = "Compiled resources to link.",
+ deprecationWarning = "Use --resources."
+ )
+ // TODO(b/64570523): Still used by blaze. Will be removed as part of the command line cleanup.
+ @Deprecated
+ public Path compiled;
+
+ @Option(
+ name = "manifest",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ defaultValue = "null",
+ converter = Converters.ExistingPathConverter.class,
+ category = "input",
+ help = "Manifest for the library.",
+ deprecationWarning = "Use --resources."
+ )
+ // TODO(b/64570523): Still used by blaze. Will be removed as part of the command line cleanup.
+ @Deprecated
+ public Path manifest;
+
+ @Option(
name = "resources",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
@@ -69,6 +98,16 @@ public class ValidateAndLinkResourcesAction {
public List<StaticLibrary> libraries;
@Option(
+ name = "packageForR",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ defaultValue = "null",
+ category = "input",
+ help = "Package for the resources."
+ )
+ public String packageForR;
+
+ @Option(
name = "staticLibraryOut",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
@@ -89,6 +128,17 @@ public class ValidateAndLinkResourcesAction {
help = "R.txt out."
)
public Path rTxtOut;
+
+ @Option(
+ name = "sourceJarOut",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ converter = Converters.PathConverter.class,
+ defaultValue = "null",
+ category = "output",
+ help = "Generated java classes from the resources."
+ )
+ public Path sourceJarOut;
}
public static void main(String[] args) throws Exception {
@@ -102,12 +152,28 @@ public class ValidateAndLinkResourcesAction {
try (ScopedTemporaryDirectory scopedTmp =
new ScopedTemporaryDirectory("android_resources_tmp")) {
+ CompiledResources resources =
+ // TODO(b/64570523): Remove when the flags are standardized.
+ Optional.ofNullable(options.resources)
+ .orElseGet(
+ () ->
+ CompiledResources.from(
+ Preconditions.checkNotNull(options.compiled),
+ Preconditions.checkNotNull(options.manifest)))
+ // We need to make the manifest aapt safe (w.r.t., placeholders). For now, just stub
+ // it out.
+ .processManifest(
+ manifest ->
+ AndroidManifestProcessor.writeDummyManifestForAapt(
+ scopedTmp.getPath().resolve("manifest-aapt-dummy/AndroidManifest.xml"),
+ options.packageForR));
ResourceLinker.create(aapt2Options.aapt2, scopedTmp.getPath())
.dependencies(Optional.ofNullable(options.deprecatedLibraries).orElse(options.libraries))
.buildVersion(aapt2Options.buildToolsVersion)
- .linkStatically(options.resources)
+ .linkStatically(resources)
.copyLibraryTo(options.staticLibraryOut)
+ .copySourceJarTo(options.sourceJarOut)
.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
index ada69bc1a3..0225041418 100644
--- 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
@@ -18,11 +18,14 @@ 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.Converters.CommaSeparatedOptionListConverter;
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.TriState;
import java.nio.file.Path;
+import java.util.List;
/** Aaprt2 specific configuration options. */
public class Aapt2ConfigOptions extends OptionsBase {
@@ -58,4 +61,100 @@ public class Aapt2ConfigOptions extends OptionsBase {
help = "Path to the android jar for resource packaging and building apks."
)
public Path androidJar;
+
+ @Option(
+ name = "annotationJar",
+ defaultValue = "null",
+ converter = ExistingPathConverter.class,
+ category = "tool",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "Path to the android jar for resource packaging and building apks."
+ )
+ public Path annotationJar;
+
+
+ @Option(
+ name = "useAaptCruncher",
+ defaultValue = "auto",
+ category = "config",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Use the legacy aapt cruncher, defaults to true for non-LIBRARY packageTypes. "
+ + " LIBRARY packages do not benefit from the additional processing as the resources"
+ + " will need to be reprocessed during the generation of the final apk. See"
+ + " https://code.google.com/p/android/issues/detail?id=67525 for a discussion of the"
+ + " different png crunching methods."
+ )
+ public TriState useAaptCruncher;
+
+ @Option(
+ name = "uncompressedExtensions",
+ defaultValue = "",
+ converter = CommaSeparatedOptionListConverter.class,
+ category = "config",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "A list of file extensions not to compress."
+ )
+ public List<String> uncompressedExtensions;
+
+ @Option(
+ name = "assetsToIgnore",
+ defaultValue = "",
+ converter = CommaSeparatedOptionListConverter.class,
+ category = "config",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "A list of assets extensions to ignore."
+ )
+ public List<String> assetsToIgnore;
+
+ @Option(
+ name = "debug",
+ defaultValue = "false",
+ category = "config",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "Indicates if it is a debug build."
+ )
+ public boolean debug;
+
+ @Option(
+ name = "resourceConfigs",
+ defaultValue = "",
+ converter = CommaSeparatedOptionListConverter.class,
+ category = "config",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "A list of resource config filters to pass to aapt."
+ )
+ public List<String> resourceConfigs;
+
+ private static final String ANDROID_SPLIT_DOCUMENTATION_URL =
+ "https://developer.android.com/guide/topics/resources/providing-resources.html"
+ + "#QualifierRules";
+
+ @Option(
+ name = "split",
+ defaultValue = "required but ignored due to allowMultiple",
+ category = "config",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ allowMultiple = true,
+ help =
+ "An individual split configuration to pass to aapt."
+ + " Each split is a list of configuration filters separated by commas."
+ + " Configuration filters are lists of configuration qualifiers separated by dashes,"
+ + " as used in resource directory names and described on the Android developer site: "
+ + ANDROID_SPLIT_DOCUMENTATION_URL
+ + " For example, a split might be 'en-television,en-xxhdpi', containing English"
+ + " assets which either are for TV screens or are extra extra high resolution."
+ + " Multiple splits can be specified by passing this flag multiple times."
+ + " Each split flag will produce an additional output file, named by replacing the"
+ + " commas in the split specification with underscores, and appending the result to"
+ + " the output package name following an underscore."
+ )
+ public List<String> splits;
}
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
index 81d94fb244..aa13dc35ce 100644
--- 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
@@ -15,6 +15,8 @@ package com.google.devtools.build.android.aapt2;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.android.ManifestContainer;
+import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
@@ -53,13 +55,21 @@ public class CompiledResources implements ManifestContainer {
return resources;
}
+ /** Copies resources archive to a path and returns the new {@link CompiledResources} */
+ public CompiledResources copyResourcesZipTo(Path destination) throws IOException {
+ return new CompiledResources(Files.copy(resources, destination), manifest, assetsDirs);
+ }
+
@Override
public Path getManifest() {
return manifest;
}
public List<String> getAssetsStrings() {
- return assetsDirs.stream().map(Path::toString).collect(Collectors.toList());
+ return assetsDirs
+ .stream()
+ .map(Path::toString)
+ .collect(Collectors.toList());
}
public CompiledResources processManifest(Function<Path, Path> processManifest) {
diff --git a/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceCompiler.java b/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceCompiler.java
index e13dc613ca..6084f6710f 100644
--- a/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceCompiler.java
+++ b/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceCompiler.java
@@ -16,6 +16,7 @@ package com.google.devtools.build.android.aapt2;
import com.android.builder.core.VariantType;
import com.android.repository.Revision;
+import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
@@ -55,22 +56,38 @@ public class ResourceCompiler {
@Override
public Path call() throws Exception {
- logger.fine(
- new AaptCommandBuilder(aapt2)
- .forBuildToolsVersion(buildToolsVersion)
- .forVariantType(VariantType.LIBRARY)
- .add("compile")
- .add("-v")
- .add("--legacy")
- .add("-o", compiledResourcesOut.toString())
- .add(file.toString())
- .execute("Compiling " + file));
+ logger.fine(
+ new AaptCommandBuilder(aapt2)
+ .forBuildToolsVersion(buildToolsVersion)
+ .forVariantType(VariantType.LIBRARY)
+ .add("compile")
+ .add("-v")
+ .add("--legacy")
+ .add("-o", compiledResourcesOut.toString())
+ .add(file.toString())
+ .execute("Compiling " + file));
+
+
String type = file.getParent().getFileName().toString();
String filename = file.getFileName().toString();
if (type.startsWith("values")) {
- filename = filename.substring(0, filename.indexOf('.')) + ".arsc";
+ filename =
+ (filename.indexOf('.') != -1 ? filename.substring(0, filename.indexOf('.')) : filename)
+ + ".arsc";
}
- return compiledResourcesOut.resolve(type + "_" + filename + ".flat");
+
+ final Path compiledResourcePath =
+ compiledResourcesOut.resolve(type + "_" + filename + ".flat");
+ Preconditions.checkArgument(
+ Files.exists(compiledResourcePath),
+ "%s does not exists after aapt2 ran.",
+ compiledResourcePath);
+ return compiledResourcePath;
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceCompiler.CompileTask(" + file + ")";
}
}
@@ -95,7 +112,8 @@ public class ResourceCompiler {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- if (!Files.isDirectory(file)) {
+ // Ignore directories and "hidden" files that start with .
+ if (!Files.isDirectory(file) && !file.getFileName().toString().startsWith(".")) {
tasks.add(
executorService.submit(
new CompileTask(
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
index fa9edc3044..dc6ab34aff 100644
--- 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
@@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.io.ByteStreams;
import com.google.devtools.build.android.AaptCommandBuilder;
+import com.google.devtools.build.android.AndroidResourceOutputs;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -40,7 +41,7 @@ public class ResourceLinker {
}
}
- private static final Logger logger = Logger.getLogger(ResourceLinker.class.getName());
+ private static Logger logger = Logger.getLogger(ResourceLinker.class.getName());
private final Path aapt2;
private final Path workingDirectory;
@@ -62,9 +63,10 @@ public class ResourceLinker {
return new ResourceLinker(aapt2, workingDirectory);
}
- /** Dependent static to be linked against. */
- public ResourceLinker dependencies(List<StaticLibrary> linkAgainst) {
- this.linkAgainst = linkAgainst;
+
+ /** Dependent static libraries to be linked to. */
+ public ResourceLinker dependencies(List<StaticLibrary> libraries) {
+ this.linkAgainst = libraries;
return this;
}
@@ -86,9 +88,8 @@ public class ResourceLinker {
public ResourceLinker filterToDensity(List<String> densitiesToFilter) {
if (densitiesToFilter.size() > 1) {
- throw new UnsupportedOperationException("Multiple densities not yet supported with aapt2");
- }
- if (densitiesToFilter.size() > 0) {
+ logger.warning("Multiple densities not yet supported with aapt2");
+ } else if (densitiesToFilter.size() > 0) {
density = Iterables.getOnlyElement(densitiesToFilter);
}
return this;
@@ -102,7 +103,9 @@ public class ResourceLinker {
*/
public StaticLibrary linkStatically(CompiledResources resources) {
final Path outPath = workingDirectory.resolve("lib.ap_");
- Path rTxt = workingDirectory.resolve("R.txt");
+ final Path rTxt = workingDirectory.resolve("R.txt");
+ final Path sourceJar = workingDirectory.resolve("r.srcjar");
+ Path javaSourceDirectory = workingDirectory.resolve("java");
try {
logger.fine(
@@ -117,12 +120,16 @@ public class ResourceLinker {
.thenAdd("--no-version-vectors")
.addRepeated("-R", unzipCompiledResources(resources.getZip()))
.addRepeated("-I", StaticLibrary.toPathStrings(linkAgainst))
+ .add("--java", javaSourceDirectory)
.add("--auto-add-overlay")
.add("-o", outPath)
- .add("--java", workingDirectory.resolve("java")) // java needed to create R.txt
+ .add("--java", javaSourceDirectory)
.add("--output-text-symbols", rTxt)
.execute(String.format("Statically linking %s", resources)));
- return StaticLibrary.from(outPath, rTxt);
+
+ AndroidResourceOutputs.createSrcJar(javaSourceDirectory, sourceJar, true /* staticIds */);
+
+ return StaticLibrary.from(outPath, rTxt, ImmutableList.of(), sourceJar);
} catch (IOException e) {
throw new LinkError(e);
}
@@ -144,7 +151,7 @@ public class ResourceLinker {
.whenVersionIsAtLeast(new Revision(23))
.thenAdd("--no-version-vectors")
.add("--no-static-lib-packages")
- .when(Level.FINE.equals(logger.getLevel()))
+ .when(logger.getLevel() == Level.FINE)
.thenAdd("-v")
.add("--manifest", compiled.getManifest())
.add("--auto-add-overlay")
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
index fc574f89ff..54ba743224 100644
--- 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
@@ -24,6 +24,7 @@ import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
+import java.util.function.Function;
import javax.annotation.Nullable;
/** A static library generated by aapt2. */
@@ -32,44 +33,74 @@ public class StaticLibrary {
private final Path library;
private final Optional<Path> rTxt;
private final Optional<List<Path>> assets;
+ private final Optional<Path> sourceJar;
- private StaticLibrary(Path library, Optional<Path> rTxt, Optional<List<Path>> assets) {
+ private StaticLibrary(
+ Path library, Optional<Path> rTxt, Optional<List<Path>> assets, Optional<Path> sourceJar) {
this.library = library;
this.rTxt = rTxt;
this.assets = assets;
+ this.sourceJar = sourceJar;
}
public static StaticLibrary from(Path library) {
- return of(library, Optional.empty(), Optional.empty());
+ return of(library, Optional.empty(), Optional.empty(), Optional.empty());
}
public static StaticLibrary from(Path library, Path rTxt) {
- return of(library, Optional.of(rTxt), Optional.empty());
+ return of(library, Optional.of(rTxt), Optional.empty(), Optional.empty());
}
- private static StaticLibrary of(Path library, Optional<Path> rTxt, Optional<List<Path>> assets) {
- return new StaticLibrary(library, rTxt, assets);
+ private static StaticLibrary of(
+ Path library, Optional<Path> rTxt, Optional<List<Path>> assets, Optional<Path> sourceJar) {
+ return new StaticLibrary(library, rTxt, assets, sourceJar);
}
- public StaticLibrary copyLibraryTo(Path target) throws IOException {
- return of(Files.copy(library, target), rTxt, assets);
+ public static StaticLibrary from(
+ Path library, Path rTxt, ImmutableList<Path> assetDirs) {
+ return of(
+ library,
+ Optional.ofNullable(rTxt),
+ Optional.ofNullable(assetDirs),
+ Optional.empty());
}
- public StaticLibrary copyRTxtTo(@Nullable final Path target) {
+ public static StaticLibrary from(
+ Path library, Path rTxt, ImmutableList<Path> assetDirs, Path sourceJar) {
return of(
library,
- rTxt.map(
- input -> {
- if (target == null) {
- return input;
- }
- try {
- return Files.copy(input, target);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }),
- assets);
+ Optional.ofNullable(rTxt),
+ Optional.ofNullable(assetDirs),
+ Optional.ofNullable(sourceJar));
+ }
+
+ public static Collection<String> toPathStrings(List<StaticLibrary> libraries) {
+ return Lists.transform(libraries, StaticLibrary::asLibraryPathString);
+ }
+
+ public StaticLibrary copyLibraryTo(Path target) throws IOException {
+ return of(Files.copy(library, target), rTxt, assets, sourceJar);
+ }
+
+ public StaticLibrary copyRTxtTo(@Nullable final Path target) {
+ return of(library, rTxt.map(copyTo(target)), assets, sourceJar);
+ }
+
+ public StaticLibrary copySourceJarTo(@Nullable final Path target) {
+ return of(library, rTxt, assets, sourceJar.map(copyTo(target)));
+ }
+
+ private Function<Path, Path> copyTo(Path target) {
+ return input -> {
+ if (target == null) {
+ return input;
+ }
+ try {
+ return Files.copy(input, target);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ };
}
@VisibleForTesting
@@ -99,14 +130,6 @@ public class StaticLibrary {
return assets.orElse(ImmutableList.of());
}
- public static Collection<String> toPathStrings(List<StaticLibrary> libraries) {
- return Lists.transform(libraries, StaticLibrary::asLibraryPathString);
- }
-
- public static StaticLibrary from(Path library, Path rTxt, ImmutableList<Path> assetDirs) {
- return of(library, Optional.ofNullable(rTxt), Optional.ofNullable(assetDirs));
- }
-
public static Collection<String> toAssetPaths(List<StaticLibrary> libraries) {
return libraries
.stream()