aboutsummaryrefslogtreecommitdiff
path: root/builder
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2012-09-11 12:35:30 -0700
committerXavier Ducrohet <xav@android.com>2012-09-26 13:48:16 -0700
commitd137d790fc023d0956397804ee2e856bd9453d22 (patch)
treef63c9ab4087ec7292e9fdb74864fa12ff5118e80 /builder
parent27973d74cf31a98eb68c6e5a8322fb4332f046fb (diff)
downloadbuild-d137d790fc023d0956397804ee2e856bd9453d22.tar.gz
Generate smaller R classes for libraries.
Using the new --output-text-symbols from aapt the build system now generates the R class for libraries manually based on the symbols exported by the libraries and the final values computed by aapt when using all the resource folders. Because only R.java is concerned, the Manifest class is now included in the library jar file. Also added a new test apps that uses instrumentation to verify the build system. Change-Id: I0e9ba124cfba729f4ab8021b440b1ac70c5d1b80
Diffstat (limited to 'builder')
-rw-r--r--builder/prebuilts/common.jarbin37233 -> 43848 bytes
-rw-r--r--builder/src/main/java/com/android/builder/AndroidBuilder.java45
-rw-r--r--builder/src/main/java/com/android/builder/SymbolLoader.java90
-rw-r--r--builder/src/main/java/com/android/builder/SymbolWriter.java103
-rw-r--r--builder/src/main/java/com/android/builder/VariantConfiguration.java32
-rw-r--r--builder/src/test/java/com/android/builder/SymbolLoaderTest.java57
-rw-r--r--builder/src/test/java/com/android/builder/SymbolWriterTest.java183
7 files changed, 479 insertions, 31 deletions
diff --git a/builder/prebuilts/common.jar b/builder/prebuilts/common.jar
index 29e054d..f6897fa 100644
--- a/builder/prebuilts/common.jar
+++ b/builder/prebuilts/common.jar
Binary files differ
diff --git a/builder/src/main/java/com/android/builder/AndroidBuilder.java b/builder/src/main/java/com/android/builder/AndroidBuilder.java
index 455608c..fd5cd28 100644
--- a/builder/src/main/java/com/android/builder/AndroidBuilder.java
+++ b/builder/src/main/java/com/android/builder/AndroidBuilder.java
@@ -16,6 +16,7 @@
package com.android.builder;
+import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
@@ -63,7 +64,7 @@ import static com.google.common.base.Preconditions.checkState;
* then build steps can be done with
* {@link #generateBuildConfig(String, java.util.List)}
* {@link #processManifest(String)}
- * {@link #processResources(String, String, String, String, String, AaptOptions)}
+ * {@link #processResources(String, String, java.util.List, String, String, String, String, AaptOptions)}
* {@link #convertBytecode(java.util.List, java.util.List, String, DexOptions)}
* {@link #packageApk(String, String, String, String)}
*
@@ -429,7 +430,7 @@ public class AndroidBuilder {
* Call this directly if you don't care about checking whether the inputs have changed.
* Otherwise, get the input first to check with {@link VariantConfiguration#getResourceInputs()}
* and then call (or not),
- * {@link #processResources(String, String, java.util.List, String, String, String, AaptOptions)}.
+ * {@link #processResources(String, String, java.util.List, String, String, String, String, AaptOptions)}.
* @param manifestFile the location of the manifest file
* @param preprocessResDir the pre-processed folder
@@ -444,11 +445,12 @@ public class AndroidBuilder {
@NonNull String manifestFile,
@Nullable String preprocessResDir,
@Nullable String sourceOutputDir,
+ @Nullable String symbolOutputDir,
@Nullable String resPackageOutput,
@Nullable String proguardOutput,
@NonNull AaptOptions options) throws IOException, InterruptedException {
List<File> inputs = mVariant.getResourceInputs();
- processResources(manifestFile, preprocessResDir, inputs, sourceOutputDir,
+ processResources(manifestFile, preprocessResDir, inputs, sourceOutputDir, symbolOutputDir,
resPackageOutput, proguardOutput, options);
}
@@ -471,6 +473,7 @@ public class AndroidBuilder {
@Nullable String preprocessResDir,
@NonNull List<File> resInputs,
@Nullable String sourceOutputDir,
+ @Nullable String symbolOutputDir,
@Nullable String resPackageOutput,
@Nullable String proguardOutput,
@NonNull AaptOptions options) throws IOException, InterruptedException {
@@ -620,13 +623,6 @@ public class AndroidBuilder {
// library specific options
if (mVariant.getType() == VariantConfiguration.Type.LIBRARY) {
command.add("--non-constant-id");
- } else {
- // only create the R class from library dependencies if this is not a library itself.
- String extraPackages = mVariant.getLibraryPackages();
- if (extraPackages != null) {
- command.add("--extra-packages");
- command.add(extraPackages);
- }
}
// AAPT options
@@ -644,9 +640,38 @@ public class AndroidBuilder {
}
}
+ List<AndroidDependency> fullLibs = mVariant.getAllLibraries();
+
+ if (symbolOutputDir != null &&
+ (mVariant.getType() == VariantConfiguration.Type.LIBRARY ||
+ (fullLibs != null && !fullLibs.isEmpty()))) {
+ command.add("--output-text-symbols");
+ command.add(symbolOutputDir);
+ }
+
mLogger.info("aapt command: %s", command.toString());
mCmdLineRunner.runCmdLine(command);
+
+ // now if the project has libraries, R needs to be created for each libraries,
+ // but only if the current project is not a library.
+ if (mVariant.getType() != VariantConfiguration.Type.LIBRARY &&
+ fullLibs != null && !fullLibs.isEmpty()) {
+ SymbolLoader symbolValues = new SymbolLoader(new File(symbolOutputDir, "R.txt"));
+ symbolValues.load();
+
+ for (AndroidDependency lib : fullLibs) {
+ SymbolLoader symbols = new SymbolLoader(new File(lib.getFolder(), "R.txt"));
+ symbols.load();
+
+ String packageName = VariantConfiguration.sManifestParser.getPackage(
+ new File(lib.getFolder(), SdkConstants.FN_ANDROID_MANIFEST_XML));
+
+ SymbolWriter writer = new SymbolWriter(sourceOutputDir, packageName,
+ symbols, symbolValues);
+ writer.write();
+ }
+ }
}
/**
diff --git a/builder/src/main/java/com/android/builder/SymbolLoader.java b/builder/src/main/java/com/android/builder/SymbolLoader.java
new file mode 100644
index 0000000..86ec4b4
--- /dev/null
+++ b/builder/src/main/java/com/android/builder/SymbolLoader.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * 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.android.builder;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ */
+class SymbolLoader {
+
+ private final File mSymbolFile;
+ private Table<String, String, SymbolEntry> mSymbols;
+
+ public static class SymbolEntry {
+ private final String mName;
+ private final String mType;
+ private final String mValue;
+
+ public SymbolEntry(String name, String type, String value) {
+ mName = name;
+ mType = type;
+ mValue = value;
+ }
+
+ public String getValue() {
+ return mValue;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getType() {
+ return mType;
+ }
+ }
+
+ SymbolLoader(File symbolFile) {
+ mSymbolFile = symbolFile;
+ }
+
+ void load() throws IOException {
+ List<String> lines = Files.readLines(mSymbolFile, Charsets.UTF_8);
+
+ mSymbols = HashBasedTable.create();
+
+ try {
+ for (String line : lines) {
+ // format is "<type> <class> <name> <value>"
+ // don't want to split on space as value could contain spaces.
+ int pos = line.indexOf(' ');
+ String type = line.substring(0, pos);
+ int pos2 = line.indexOf(' ', pos + 1);
+ String className = line.substring(pos + 1, pos2);
+ int pos3 = line.indexOf(' ', pos2 + 1);
+ String name = line.substring(pos2 + 1, pos3);
+ String value = line.substring(pos3 + 1);
+
+ mSymbols.put(className, name, new SymbolEntry(name, type, value));
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IOException("File format error reading " + mSymbolFile.getAbsolutePath());
+ }
+ }
+
+ Table<String, String, SymbolEntry> getSymbols() {
+ return mSymbols;
+ }
+}
diff --git a/builder/src/main/java/com/android/builder/SymbolWriter.java b/builder/src/main/java/com/android/builder/SymbolWriter.java
new file mode 100644
index 0000000..0c796f6
--- /dev/null
+++ b/builder/src/main/java/com/android/builder/SymbolWriter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * 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.android.builder;
+
+import com.android.SdkConstants;
+import com.android.builder.SymbolLoader.SymbolEntry;
+import com.google.common.base.Charsets;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Table;
+import com.google.common.io.Closeables;
+import com.google.common.io.Files;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ */
+public class SymbolWriter {
+
+ private final String mOutFolder;
+ private final String mPackageName;
+ private final SymbolLoader mSymbols;
+ private final SymbolLoader mValues;
+
+ SymbolWriter(String outFolder, String packageName, SymbolLoader symbols, SymbolLoader values) {
+ mOutFolder = outFolder;
+ mPackageName = packageName;
+ mSymbols = symbols;
+ mValues = values;
+ }
+
+ void write() throws IOException {
+ Splitter splitter = Splitter.on('.');
+ Iterable<String> folders = splitter.split(mPackageName);
+ File file = new File(mOutFolder);
+ for (String folder : folders) {
+ file = new File(file, folder);
+ }
+ file.mkdirs();
+ file = new File(file, SdkConstants.FN_RESOURCE_CLASS);
+
+ BufferedWriter writer = null;
+ try {
+ writer = Files.newWriter(file, Charsets.UTF_8);
+
+ writer.write("/* AUTO-GENERATED FILE. DO NOT MODIFY.\n");
+ writer.write(" *\n");
+ writer.write(" * This class was automatically generated by the\n");
+ writer.write(" * aapt tool from the resource data it found. It\n");
+ writer.write(" * should not be modified by hand.\n");
+ writer.write(" */\n");
+
+ writer.write("package ");
+ writer.write(mPackageName);
+ writer.write(";\n\npublic final class R {\n");
+
+ Table<String, String, SymbolEntry> symbols = mSymbols.getSymbols();
+ Table<String, String, SymbolEntry> values = mValues.getSymbols();
+
+ for (String row : symbols.rowKeySet()) {
+ writer.write("\tpublic static final class ");
+ writer.write(row);
+ writer.write(" {\n");
+
+ for (Map.Entry<String, SymbolEntry> symbol : symbols.row(row).entrySet()) {
+ // get the matching SymbolEntry from the values Table.
+ SymbolEntry value = values.get(row, symbol.getKey());
+ if (value != null) {
+ writer.write("\t\tpublic static final ");
+ writer.write(value.getType());
+ writer.write(" ");
+ writer.write(value.getName());
+ writer.write(" = ");
+ writer.write(value.getValue());
+ writer.write(";\n");
+ }
+ }
+
+ writer.write("\t}\n");
+ }
+
+ writer.write("}\n");
+ } finally {
+ Closeables.closeQuietly(writer);
+ }
+ }
+}
diff --git a/builder/src/main/java/com/android/builder/VariantConfiguration.java b/builder/src/main/java/com/android/builder/VariantConfiguration.java
index aac7e79..531f5a2 100644
--- a/builder/src/main/java/com/android/builder/VariantConfiguration.java
+++ b/builder/src/main/java/com/android/builder/VariantConfiguration.java
@@ -34,7 +34,7 @@ import static com.google.common.base.Preconditions.checkState;
*/
public class VariantConfiguration {
- private final static ManifestParser sManifestParser = new DefaultManifestParser();
+ final static ManifestParser sManifestParser = new DefaultManifestParser();
private final ProductFlavor mDefaultConfig;
private final SourceSet mDefaultSourceSet;
@@ -174,26 +174,6 @@ public class VariantConfiguration {
resolveIndirectLibraryDependencies(mDirectLibraries, mFlatLibraries);
}
- public String getLibraryPackages() {
- if (mFlatLibraries.isEmpty()) {
- return null;
- }
-
- StringBuilder sb = new StringBuilder();
-
- for (AndroidDependency dep : mFlatLibraries) {
- File manifest = dep.getManifest();
- String packageName = sManifestParser.getPackage(manifest);
- if (sb.length() > 0) {
- sb.append(':');
- }
- sb.append(packageName);
- }
-
- return sb.toString();
- }
-
-
public void setOutput(AndroidDependency output) {
mOutput = output;
}
@@ -237,10 +217,20 @@ public class VariantConfiguration {
return !mDirectLibraries.isEmpty();
}
+ /**
+ * Returns the direct library dependencies
+ */
public List<AndroidDependency> getDirectLibraries() {
return mDirectLibraries;
}
+ /**
+ * Returns all the library dependencies, direct and transitive.
+ */
+ public List<AndroidDependency> getAllLibraries() {
+ return mFlatLibraries;
+ }
+
public Type getType() {
return mType;
}
diff --git a/builder/src/test/java/com/android/builder/SymbolLoaderTest.java b/builder/src/test/java/com/android/builder/SymbolLoaderTest.java
new file mode 100644
index 0000000..17b5cb6
--- /dev/null
+++ b/builder/src/test/java/com/android/builder/SymbolLoaderTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.android.builder;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.Table;
+import com.google.common.io.Files;
+import junit.framework.TestCase;
+
+import java.io.File;
+
+@SuppressWarnings("javadoc")
+public class SymbolLoaderTest extends TestCase {
+ public void test() throws Exception {
+ String r = "" +
+ "int xml authenticator 0x7f040000\n";
+ File file = File.createTempFile(getClass().getSimpleName(), "txt");
+ file.deleteOnExit();
+ Files.write(r, file, Charsets.UTF_8);
+ SymbolLoader loader = new SymbolLoader(file);
+ loader.load();
+ Table<String, String, SymbolLoader.SymbolEntry> symbols = loader.getSymbols();
+ assertNotNull(symbols);
+ assertEquals(1, symbols.size());
+ assertNotNull(symbols.get("xml", "authenticator"));
+ assertEquals("0x7f040000", symbols.get("xml", "authenticator").getValue());
+ }
+
+ public void testStyleables() throws Exception {
+ String r = "" +
+ "int[] styleable LimitedSizeLinearLayout { 0x7f010000, 0x7f010001 }\n" +
+ "int styleable LimitedSizeLinearLayout_max_height 1\n" +
+ "int styleable LimitedSizeLinearLayout_max_width 0\n" +
+ "int xml authenticator 0x7f040000\n";
+ File file = File.createTempFile(getClass().getSimpleName(), "txt");
+ file.deleteOnExit();
+ Files.write(r, file, Charsets.UTF_8);
+ SymbolLoader loader = new SymbolLoader(file);
+ loader.load();
+ Table<String, String, SymbolLoader.SymbolEntry> symbols = loader.getSymbols();
+ assertNotNull(symbols);
+ assertEquals(4, symbols.size());
+ }
+}
diff --git a/builder/src/test/java/com/android/builder/SymbolWriterTest.java b/builder/src/test/java/com/android/builder/SymbolWriterTest.java
new file mode 100644
index 0000000..7285ee3
--- /dev/null
+++ b/builder/src/test/java/com/android/builder/SymbolWriterTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.android.builder;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.Table;
+import com.google.common.io.Files;
+import junit.framework.TestCase;
+
+import java.io.File;
+
+@SuppressWarnings("javadoc")
+public class SymbolWriterTest extends TestCase {
+ private void check(String packageName, String rText, String rJava) throws Exception {
+ // Load symbols
+ File file = File.createTempFile(getClass().getSimpleName(), "txt");
+ file.deleteOnExit();
+ Files.write(rText, file, Charsets.UTF_8);
+ SymbolLoader loader = new SymbolLoader(file);
+ loader.load();
+ Table<String, String, SymbolLoader.SymbolEntry> symbols = loader.getSymbols();
+ assertNotNull(symbols);
+
+ // Write symbols
+ File outFolder = Files.createTempDir();
+ outFolder.mkdirs();
+
+ SymbolWriter writer = new SymbolWriter(outFolder.getPath(), packageName, loader, loader);
+ writer.write();
+
+ String contents = Files.toString(new File(outFolder,
+ packageName.replace('.', File.separatorChar) + File.separator + "R.java"),
+ Charsets.UTF_8);
+
+ // Ensure we wrote what was expected
+ assertEquals(rJava, contents.replaceAll("\t", " "));
+ }
+
+ public void test1() throws Exception {
+ check(
+ // Package
+ "test.pkg",
+
+ // R.txt
+ "int xml authenticator 0x7f040000\n",
+
+ // R.java
+ "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n" +
+ " *\n" +
+ " * This class was automatically generated by the\n" +
+ " * aapt tool from the resource data it found. It\n" +
+ " * should not be modified by hand.\n" +
+ " */\n" +
+ "package test.pkg;\n" +
+ "\n" +
+ "public final class R {\n" +
+ " public static final class xml {\n" +
+ " public static final int authenticator = 0x7f040000;\n" +
+ " }\n" +
+ "}\n"
+ );
+ }
+
+ public void test2() throws Exception {
+ check(
+ // Package
+ "test.pkg",
+
+ // R.txt
+ "int drawable foobar 0x7f020000\n" +
+ "int drawable ic_launcher 0x7f020001\n" +
+ "int string app_name 0x7f030000\n" +
+ "int string lib1 0x7f030001\n" +
+ "int style AppBaseTheme 0x7f040000\n" +
+ "int style AppTheme 0x7f040001\n",
+
+ // R.java
+ "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n" +
+ " *\n" +
+ " * This class was automatically generated by the\n" +
+ " * aapt tool from the resource data it found. It\n" +
+ " * should not be modified by hand.\n" +
+ " */\n" +
+ "package test.pkg;\n" +
+ "\n" +
+ "public final class R {\n" +
+ " public static final class style {\n" +
+ " public static final int AppBaseTheme = 0x7f040000;\n" +
+ " public static final int AppTheme = 0x7f040001;\n" +
+ " }\n" +
+ " public static final class string {\n" +
+ " public static final int app_name = 0x7f030000;\n" +
+ " public static final int lib1 = 0x7f030001;\n" +
+ " }\n" +
+ " public static final class drawable {\n" +
+ " public static final int ic_launcher = 0x7f020001;\n" +
+ " public static final int foobar = 0x7f020000;\n" +
+ " }\n" +
+ "}\n"
+ );
+ }
+
+ public void testStyleables1() throws Exception {
+ check(
+ // Package
+ "test.pkg",
+
+ // R.txt
+ "int[] styleable TiledView { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004 }\n" +
+ "int styleable TiledView_tileName 2\n" +
+ "int styleable TiledView_tilingEnum 4\n" +
+ "int styleable TiledView_tilingMode 3\n" +
+ "int styleable TiledView_tilingProperty 0\n" +
+ "int styleable TiledView_tilingResource 1\n",
+
+ // R.java
+ "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n" +
+ " *\n" +
+ " * This class was automatically generated by the\n" +
+ " * aapt tool from the resource data it found. It\n" +
+ " * should not be modified by hand.\n" +
+ " */\n" +
+ "package test.pkg;\n" +
+ "\n" +
+ "public final class R {\n" +
+ " public static final class styleable {\n" +
+ " public static final int TiledView_tilingProperty = 0;\n" +
+ " public static final int TiledView_tilingMode = 3;\n" +
+ " public static final int TiledView_tilingResource = 1;\n" +
+ " public static final int TiledView_tileName = 2;\n" +
+ " public static final int TiledView_tilingEnum = 4;\n" +
+ " public static final int[] TiledView = { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004 };\n" +
+ " }\n" +
+ "}\n"
+ );
+ }
+
+ public void testStyleables2() throws Exception {
+ check(
+ // Package
+ "test.pkg",
+
+ // R.txt
+ "int[] styleable LimitedSizeLinearLayout { 0x7f010000, 0x7f010001 }\n" +
+ "int styleable LimitedSizeLinearLayout_max_height 1\n" +
+ "int styleable LimitedSizeLinearLayout_max_width 0\n" +
+ "int xml authenticator 0x7f040000\n",
+
+ // R.java
+ "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n" +
+ " *\n" +
+ " * This class was automatically generated by the\n" +
+ " * aapt tool from the resource data it found. It\n" +
+ " * should not be modified by hand.\n" +
+ " */\n" +
+ "package test.pkg;\n" +
+ "\n" +
+ "public final class R {\n" +
+ " public static final class styleable {\n" +
+ " public static final int LimitedSizeLinearLayout_max_height = 1;\n" +
+ " public static final int LimitedSizeLinearLayout_max_width = 0;\n" +
+ " public static final int[] LimitedSizeLinearLayout = { 0x7f010000, 0x7f010001 };\n" +
+ " }\n" +
+ " public static final class xml {\n" +
+ " public static final int authenticator = 0x7f040000;\n" +
+ " }\n" +
+ "}\n"
+ );
+ }
+}