diff options
author | Googler <noreply@google.com> | 2016-12-03 16:36:18 +0000 |
---|---|---|
committer | Damien Martin-Guillerez <dmarting@google.com> | 2016-12-05 10:21:45 +0000 |
commit | 758f81faf1d9343e1fd1f0da3fb50b772ca758f4 (patch) | |
tree | fd0c19b06f0592785b815bfcd9dc64fcdb505d19 | |
parent | d3e2d0438b72c5bb2b766330c15ca916c0e1a89c (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
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(); } - } |