aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2016-12-03 16:36:18 +0000
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2016-12-05 10:21:45 +0000
commit758f81faf1d9343e1fd1f0da3fb50b772ca758f4 (patch)
treefd0c19b06f0592785b815bfcd9dc64fcdb505d19
parentd3e2d0438b72c5bb2b766330c15ca916c0e1a89c (diff)
Fix NPE in RClassGenerator when dev has code in unnamed package
Just put the R class in the unnamed package. If we disable the RClassGenerator and use the old code path, we would have constructed a cmdline flag set to "null" for aapt, and aapt would write "package null;" which also wouldn't work... This may allow you to build an android_library but it may be hard to build an android_binary because ultimately AAPT will reject an empty "package" attribute in the AndroidManifest.xml when you try to build the APK, so you'd need to stamp that differently. -- PiperOrigin-RevId: 140945125 MOS_MIGRATED_REVID=140945125
-rw-r--r--src/test/java/com/google/devtools/build/android/resources/RClassGeneratorTest.java25
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceMergingAction.java4
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java70
3 files changed, 54 insertions, 45 deletions
diff --git a/src/test/java/com/google/devtools/build/android/resources/RClassGeneratorTest.java b/src/test/java/com/google/devtools/build/android/resources/RClassGeneratorTest.java
index 0b53d86264..deadf2831f 100644
--- a/src/test/java/com/google/devtools/build/android/resources/RClassGeneratorTest.java
+++ b/src/test/java/com/google/devtools/build/android/resources/RClassGeneratorTest.java
@@ -299,6 +299,31 @@ public class RClassGeneratorTest {
);
}
+ @Test
+ public void emptyPackage() throws Exception {
+ boolean finalFields = true;
+ // Make sure we handle an empty package string.
+ SymbolLoader symbolValues = createSymbolFile("R.txt", "int string some_string 0x7f200000");
+ SymbolLoader symbolsInLibrary = symbolValues;
+ Path out = temp.resolve("classes");
+ Files.createDirectories(out);
+ RClassGenerator writer =
+ RClassGenerator.fromSymbols(
+ out, "", symbolValues, ImmutableList.of(symbolsInLibrary), finalFields);
+ writer.write();
+
+ Path packageDir = out.resolve("");
+ checkFilesInPackage(packageDir, "R.class", "R$string.class");
+ Class<?> outerClass = checkTopLevelClass(out, "R", "R$string");
+ checkInnerClass(
+ out,
+ "R$string",
+ outerClass,
+ ImmutableMap.of("some_string", 0x7f200000),
+ ImmutableMap.<String, List<Integer>>of(),
+ finalFields);
+ }
+
// Test utilities
private Path createFile(String name, String... contents) throws IOException {
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMergingAction.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMergingAction.java
index 0b90f6fc3d..e2a800d584 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMergingAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMergingAction.java
@@ -20,6 +20,7 @@ import com.android.ide.common.res2.MergingException;
import com.android.utils.StdLogger;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
+import com.google.common.base.Strings;
import com.google.common.io.Files;
import com.google.devtools.build.android.AndroidResourceProcessor.AaptConfigOptions;
import com.google.devtools.build.android.Converters.ExistingPathConverter;
@@ -162,12 +163,11 @@ public class AndroidResourceMergingAction {
logger.fine(String.format("Setup finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
VariantType packageType = VariantType.LIBRARY;
- String packageName = options.packageForR;
AndroidResourceClassWriter resourceClassWriter =
new AndroidResourceClassWriter(
new AndroidFrameworkAttrIdJar(aaptConfigOptions.androidJar),
generatedSources,
- packageName);
+ Strings.nullToEmpty(options.packageForR));
resourceClassWriter.setIncludeClassFile(true);
resourceClassWriter.setIncludeJavaFile(false);
diff --git a/src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java b/src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java
index d1ad740027..5342a83901 100644
--- a/src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java
+++ b/src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java
@@ -71,11 +71,12 @@ public class RClassGenerator {
String packageName,
SymbolLoader values,
Collection<SymbolLoader> packageSymbols,
- boolean finalFields) throws IOException {
+ boolean finalFields)
+ throws IOException {
Table<String, String, SymbolEntry> symbolsTable = getAllSymbols(packageSymbols);
Table<String, String, SymbolEntry> valuesTable = getSymbols(values);
- Map<ResourceType, List<FieldInitializer>> initializers = getInitializers(symbolsTable,
- valuesTable);
+ Map<ResourceType, List<FieldInitializer>> initializers =
+ getInitializers(symbolsTable, valuesTable);
return new RClassGenerator(outFolder, packageName, initializers, finalFields);
}
@@ -99,8 +100,7 @@ public class RClassGenerator {
}
private static Table<String, String, SymbolEntry> getAllSymbols(
- Collection<SymbolLoader> symbolLoaders)
- throws IOException {
+ Collection<SymbolLoader> symbolLoaders) throws IOException {
Table<String, String, SymbolEntry> symbols = HashBasedTable.create();
for (SymbolLoader symbolLoader : symbolLoaders) {
symbols.putAll(getSymbols(symbolLoader));
@@ -108,7 +108,6 @@ public class RClassGenerator {
return symbols;
}
-
private static Table<String, String, SymbolEntry> getSymbols(SymbolLoader symbolLoader)
throws IOException {
// TODO(bazel-team): remove when we update android_ide_common to a version w/ public visibility
@@ -116,20 +115,17 @@ public class RClassGenerator {
Method getSymbols = SymbolLoader.class.getDeclaredMethod("getSymbols");
getSymbols.setAccessible(true);
@SuppressWarnings("unchecked")
- Table<String, String, SymbolEntry> result = (Table<String, String, SymbolEntry>)
- getSymbols.invoke(symbolLoader);
+ Table<String, String, SymbolEntry> result =
+ (Table<String, String, SymbolEntry>) getSymbols.invoke(symbolLoader);
return result;
} catch (ReflectiveOperationException e) {
throw new IOException(e);
}
}
- /**
- * Convert the {@link SymbolLoader} data, to a map of {@link FieldInitializer}.
- */
+ /** Convert the {@link SymbolLoader} data, to a map of {@link FieldInitializer}. */
private static Map<ResourceType, List<FieldInitializer>> getInitializers(
- Table<String, String, SymbolEntry> symbols,
- Table<String, String, SymbolEntry> values) {
+ Table<String, String, SymbolEntry> symbols, Table<String, String, SymbolEntry> values) {
Map<ResourceType, List<FieldInitializer>> initializers = new EnumMap<>(ResourceType.class);
for (String typeName : symbols.rowKeySet()) {
ResourceType resourceType = ResourceType.getEnum(typeName);
@@ -156,22 +152,20 @@ public class RClassGenerator {
initializers.add(IntFieldInitializer.of(value.getName(), value.getValue()));
} else {
Preconditions.checkArgument(value.getType().equals("int[]"));
- initializers
- .add(IntArrayFieldInitializer.of(value.getName(), value.getValue()));
+ initializers.add(IntArrayFieldInitializer.of(value.getName(), value.getValue()));
}
} else {
// Value may be missing if resource overriding eliminates resources at the binary
// level, which were originally present at the library level.
- logger.fine(String.format("Skipping R.%s.%s -- value not known in binary's R.txt",
- typeName, symbolName));
+ logger.fine(
+ String.format(
+ "Skipping R.%s.%s -- value not known in binary's R.txt", typeName, symbolName));
}
}
return initializers;
}
- /**
- * Builds the bytecode and writes out the R.class file, and R$inner.class files.
- */
+ /** Builds the bytecode and writes out the R.class file, and R$inner.class files. */
public void write() throws IOException {
Iterable<String> folders = PACKAGE_SPLITTER.split(packageName);
Path packageDir = outFolder;
@@ -187,7 +181,7 @@ public class RClassGenerator {
Path rClassFile = packageDir.resolve(SdkConstants.FN_COMPILED_RESOURCE_CLASS);
String packageWithSlashes = packageName.replaceAll("\\.", "/");
- String rClassName = packageWithSlashes + "/R";
+ String rClassName = packageWithSlashes.isEmpty() ? "R" : (packageWithSlashes + "/R");
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
classWriter.visit(
JAVA_VERSION,
@@ -221,7 +215,8 @@ public class RClassGenerator {
List<FieldInitializer> initializers,
Path packageDir,
String fullyQualifiedOuterClass,
- String innerClass) throws IOException {
+ String innerClass)
+ throws IOException {
ClassWriter innerClassWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
String fullyQualifiedInnerClass =
writeInnerClassHeader(fullyQualifiedOuterClass, innerClass, innerClassWriter);
@@ -245,8 +240,8 @@ public class RClassGenerator {
Files.write(innerFile, innerClassWriter.toByteArray());
}
- private String writeInnerClassHeader(String fullyQualifiedOuterClass, String innerClass,
- ClassWriter innerClassWriter) {
+ private String writeInnerClassHeader(
+ String fullyQualifiedOuterClass, String innerClass, ClassWriter innerClassWriter) {
String fullyQualifiedInnerClass = fullyQualifiedOuterClass + "$" + innerClass;
innerClassWriter.visit(
JAVA_VERSION,
@@ -266,12 +261,9 @@ public class RClassGenerator {
}
private static void writeConstructor(ClassWriter classWriter) {
- MethodVisitor constructor = classWriter.visitMethod(
- Opcodes.ACC_PUBLIC,
- "<init>",
- "()V",
- null, /* signature */
- null /* exceptions */);
+ MethodVisitor constructor =
+ classWriter.visitMethod(
+ Opcodes.ACC_PUBLIC, "<init>", "()V", null, /* signature */ null /* exceptions */);
constructor.visitCode();
constructor.visitVarInsn(Opcodes.ALOAD, 0);
constructor.visitMethodInsn(Opcodes.INVOKESPECIAL, SUPER_CLASS, "<init>", "()V", false);
@@ -281,26 +273,18 @@ public class RClassGenerator {
}
private static void writeStaticClassInit(
- ClassWriter classWriter,
- String className,
- List<FieldInitializer> initializers) {
- MethodVisitor visitor = classWriter.visitMethod(
- Opcodes.ACC_STATIC,
- "<clinit>",
- "()V",
- null, /* signature */
- null /* exceptions */);
+ ClassWriter classWriter, String className, List<FieldInitializer> initializers) {
+ MethodVisitor visitor =
+ classWriter.visitMethod(
+ Opcodes.ACC_STATIC, "<clinit>", "()V", null, /* signature */ null /* exceptions */);
visitor.visitCode();
int stackSlotsNeeded = 0;
InstructionAdapter insts = new InstructionAdapter(visitor);
for (FieldInitializer fieldInit : initializers) {
- stackSlotsNeeded = Math.max(
- stackSlotsNeeded,
- fieldInit.writeCLInit(insts, className));
+ stackSlotsNeeded = Math.max(stackSlotsNeeded, fieldInit.writeCLInit(insts, className));
}
insts.areturn(Type.VOID_TYPE);
visitor.visitMaxs(stackSlotsNeeded, 0);
visitor.visitEnd();
}
-
}