Stop bundling Robolectric Processor's sdks.txt file as a Java resource
The resources approach would be difficult to get working in Gradle/Maven for
custom shadow packages outside of the Robolectric project itself. This is
because in Gradle/Maven, the the processor jar file currently contains a
sdks.txt resource that has hardcoded paths from the releaser's workstation.
Instead switch to a file-based scheme and emit the sdks.txt file to
Robolectric's root project's build directory. With a file based scheme is is
possible for external projects to specify an sdks.txt file path as well.
Also turn off the ImplementsValidator plugin off by default as it is
non-trivial for custom shadow packages in the Gradle/Maven world to generate
sdks.txt files. This validation is primarily intended for the Robolectric
project itself.
Fixes #4801
PiperOrigin-RevId: 344144378
diff --git a/processor/build.gradle b/processor/build.gradle
index 219aabc..e207327 100644
--- a/processor/build.gradle
+++ b/processor/build.gradle
@@ -22,10 +22,7 @@
}
task('generateSdksFile', type: GenerateSdksFileTask) {
- dependsOn(tasks['processResources'])
-
- File outDir = project.sourceSets['main'].output.resourcesDir
- outFile = new File(outDir, 'sdks.txt')
+ outFile = new File(project.rootProject.buildDir, 'sdks.txt')
}
tasks['classes'].dependsOn(generateSdksFile)
diff --git a/processor/src/main/java/org/robolectric/annotation/processing/RobolectricProcessor.java b/processor/src/main/java/org/robolectric/annotation/processing/RobolectricProcessor.java
index fed51c9..15e4139 100644
--- a/processor/src/main/java/org/robolectric/annotation/processing/RobolectricProcessor.java
+++ b/processor/src/main/java/org/robolectric/annotation/processing/RobolectricProcessor.java
@@ -134,7 +134,7 @@
this.jsonDocsEnabled = "true".equalsIgnoreCase(options.get(JSON_DOCS_ENABLED));
this.sdkCheckMode =
SdkCheckMode.valueOf(options.getOrDefault(SDK_CHECK_MODE, "WARN").toUpperCase());
- this.sdksFile = options.getOrDefault(SDKS_FILE, "/sdks.txt");
+ this.sdksFile = getSdksFile(options, SDKS_FILE);
this.priority =
Integer.parseInt(options.getOrDefault(PRIORITY, "0"));
@@ -144,6 +144,13 @@
}
}
+ /**
+ * Extendable to support Bazel environments, where the sdks file is generated as a build artifact.
+ */
+ protected String getSdksFile(Map<String, String> options, String sdksFileParam) {
+ return options.get(sdksFileParam);
+ }
+
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
diff --git a/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java b/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java
index 34b53e8..4c6c592 100644
--- a/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java
+++ b/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java
@@ -5,14 +5,18 @@
import static org.robolectric.annotation.processing.validator.ImplementsValidator.STATIC_INITIALIZER_METHOD_NAME;
import static org.robolectric.annotation.processing.validator.ImplementsValidator.getClassFQName;
+import com.google.common.collect.ImmutableList;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -37,6 +41,7 @@
import org.objectweb.asm.tree.MethodNode;
import org.robolectric.annotation.Implementation;
+/** Encapsulates a collection of Android framework jars. */
public class SdkStore {
private final Set<Sdk> sdks = new TreeSet<>();
@@ -83,10 +88,14 @@
}
}
- private static List<Sdk> loadFromSdksFile(String resourceFileName) {
- try (InputStream resIn = SdkStore.class.getResourceAsStream(resourceFileName)) {
+ private static ImmutableList<Sdk> loadFromSdksFile(String fileName) {
+ if (fileName == null || Files.notExists(Paths.get(fileName))) {
+ return ImmutableList.of();
+ }
+
+ try (InputStream resIn = new FileInputStream(fileName)) {
if (resIn == null) {
- throw new RuntimeException("no such resource " + resourceFileName);
+ throw new RuntimeException("no such file " + fileName);
}
BufferedReader in =
@@ -98,9 +107,9 @@
sdks.add(new Sdk(line));
}
}
- return sdks;
+ return ImmutableList.copyOf(sdks);
} catch (IOException e) {
- throw new RuntimeException("failed reading " + resourceFileName, e);
+ throw new RuntimeException("failed reading " + fileName, e);
}
}
@@ -182,8 +191,9 @@
return null;
}
- private boolean suppressWarnings(ExecutableElement methodElement, String warningName) {
- SuppressWarnings[] suppressWarnings = methodElement.getAnnotationsByType(SuppressWarnings.class);
+ private static boolean suppressWarnings(ExecutableElement methodElement, String warningName) {
+ SuppressWarnings[] suppressWarnings =
+ methodElement.getAnnotationsByType(SuppressWarnings.class);
for (SuppressWarnings suppression : suppressWarnings) {
for (String name : suppression.value()) {
if (warningName.equals(name)) {
@@ -194,12 +204,13 @@
return false;
}
- private boolean typeIsNumeric(MethodExtraInfo sdkMethod, MethodExtraInfo implMethod) {
+ private static boolean typeIsNumeric(MethodExtraInfo sdkMethod, MethodExtraInfo implMethod) {
return implMethod.returnType.equals("java.lang.Number")
&& isNumericType(sdkMethod.returnType);
}
- private boolean typeIsOkForLooseSignatures(MethodExtraInfo implMethod, MethodExtraInfo sdkMethod) {
+ private static boolean typeIsOkForLooseSignatures(
+ MethodExtraInfo implMethod, MethodExtraInfo sdkMethod) {
return
// loose signatures allow a return type of Object...
implMethod.returnType.equals("java.lang.Object")
@@ -208,7 +219,7 @@
&& sdkMethod.returnType.endsWith("[]"));
}
- private boolean isNumericType(String type) {
+ private static boolean isNumericType(String type) {
return type.equals("int") || type.equals("long");
}
@@ -378,7 +389,7 @@
}
}
- private String cleanMethodName(ExecutableElement methodElement) {
+ private static String cleanMethodName(ExecutableElement methodElement) {
String name = methodElement.getSimpleName().toString();
if (CONSTRUCTOR_METHOD_NAME.equals(name)) {
return "<init>";
@@ -446,8 +457,7 @@
return false;
}
MethodExtraInfo that = (MethodExtraInfo) o;
- return isStatic == that.isStatic &&
- Objects.equals(returnType, that.returnType);
+ return isStatic == that.isStatic && Objects.equals(returnType, that.returnType);
}
@Override