Bug 1384312 - Part 1: Move omg.annotation; add stub Gradle annotationProcessor. r=maliu
This is work in progress. It builds on work to use newer Android
build-tools and Android-Gradle plugin and adds a stub
annotationProcessor. I cloned and hacked up the example processor at
https://github.com/erdemtopak/simple-annotation-processor, and it
seems to work.
Max: the next steps are:
- move the annotation processor sources from
`build/annotationProcessors` to `mobile/android/processor`
- update `build/annotationProcessors/moz.build`
- factor the existing annotationProcessor into an annotation processor
_and_ a command line app
- perhaps add unit tests to `mobile/android/processor/src/test/*`,
like any other (Java) Gradle project
MozReview-Commit-ID: BczfZOu3h4h
new file mode 100644
--- /dev/null
+++ b/mobile/android/annotations/build.gradle
@@ -0,0 +1,10 @@
+buildDir "${topobjdir}/gradle/build/mobile/android/annotations"
+
+apply plugin: 'java'
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+}
+
+sourceCompatibility = JavaVersion.VERSION_1_7
+targetCompatibility = JavaVersion.VERSION_1_7
rename from mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/BuildFlag.java
rename to mobile/android/annotations/src/main/java/org/mozilla/gecko/annotation/BuildFlag.java
rename from mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/JNITarget.java
rename to mobile/android/annotations/src/main/java/org/mozilla/gecko/annotation/JNITarget.java
rename from mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/ReflectionTarget.java
rename to mobile/android/annotations/src/main/java/org/mozilla/gecko/annotation/ReflectionTarget.java
rename from mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/RobocopTarget.java
rename to mobile/android/annotations/src/main/java/org/mozilla/gecko/annotation/RobocopTarget.java
rename from mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/WebRTCJNITarget.java
rename to mobile/android/annotations/src/main/java/org/mozilla/gecko/annotation/WebRTCJNITarget.java
rename from mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/WrapForJNI.java
rename to mobile/android/annotations/src/main/java/org/mozilla/gecko/annotation/WrapForJNI.java
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -218,16 +218,19 @@ android {
// we have tests that start test servers and the bound ports
// collide. We'll fix this soon to have much faster test cycles.
maxParallelForks 1
}
}
}
dependencies {
+ compile project(':annotations')
+ annotationProcessor project(':processor')
+
compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
compile "com.android.support:appcompat-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
compile "com.android.support:cardview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
compile "com.android.support:recyclerview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
compile "com.android.support:design:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
compile "com.android.support:customtabs:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
compile "com.android.support:palette-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -137,28 +137,31 @@ y = GENERATED_FILES['generated/preproces
y.script = 'generate_build_config.py:generate_java'
y.inputs += ['MmaConstants.java.in']
z = GENERATED_FILES['AndroidManifest.xml']
z.script = 'generate_build_config.py:generate_android_manifest'
z.inputs += ['AndroidManifest.xml.in']
include('android-services.mozbuild')
+annotations_source_dir = TOPSRCDIR + '/mobile/android/annotations/src/main/'
geckoview_source_dir = TOPSRCDIR + '/mobile/android/geckoview/src/main/'
geckoview_thirdparty_source_dir = TOPSRCDIR + '/mobile/android/geckoview/src/thirdparty/'
thirdparty_source_dir = TOPSRCDIR + '/mobile/android/thirdparty/'
constants_jar = add_java_jar('constants')
-constants_jar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x for x in [
+constants_jar.sources += [annotations_source_dir + 'java/org/mozilla/gecko/' + x for x in [
'annotation/BuildFlag.java',
'annotation/JNITarget.java',
'annotation/ReflectionTarget.java',
'annotation/RobocopTarget.java',
'annotation/WebRTCJNITarget.java',
'annotation/WrapForJNI.java',
+]]
+constants_jar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x for x in [
'SysInfo.java',
]]
constants_jar.sources += ['java/org/mozilla/gecko/' + x for x in [
'adjust/AdjustHelperInterface.java',
'adjust/AttributionHelperListener.java',
'db/BrowserContract.java',
'LocaleManager.java',
'Locales.java',
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -98,16 +98,18 @@ android {
assets {
}
}
}
}
dependencies {
+ compile project(':annotations')
+ annotationProcessor project(':processor')
compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
}
task syncPreprocessedCode(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
into("${project.buildDir}/generated/source/preprocessed_code")
from("${topobjdir}/mobile/android/base/generated/preprocessed") {
// These constants files are included in the main app project.
exclude '**/AdjustConstants.java'
new file mode 100644
--- /dev/null
+++ b/mobile/android/processor/build.gradle
@@ -0,0 +1,11 @@
+buildDir "${topobjdir}/gradle/build/mobile/android/processor"
+
+apply plugin: 'java'
+
+dependencies {
+ compile project (':annotations')
+ compile 'com.google.auto.service:auto-service:1.0-rc3'
+}
+
+sourceCompatibility = JavaVersion.VERSION_1_7
+targetCompatibility = JavaVersion.VERSION_1_7
new file mode 100644
--- /dev/null
+++ b/mobile/android/processor/src/main/java/org/mozilla/gecko/annotation/processor/AnnotatedRandomElement.java
@@ -0,0 +1,58 @@
+package org.mozilla.gecko.annotation.processor;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+abstract class AnnotatedRandomElement {
+
+ Element element;
+ Name qualifiedClassName;
+ Name simpleClassName;
+ Name elementName;
+ TypeMirror elementType;
+
+ AnnotatedRandomElement(Element element) {
+ this.element = element;
+ elementName = element.getSimpleName();
+ simpleClassName = element.getEnclosingElement().getSimpleName();
+ TypeElement enclosingElement = ((TypeElement) element.getEnclosingElement());
+ qualifiedClassName = enclosingElement.getQualifiedName();
+ elementType = element.asType();
+ }
+
+ Name getQualifiedClassName() {
+ return qualifiedClassName;
+ }
+
+ Name getSimpleClassName() {
+ return simpleClassName;
+ }
+
+ Name getElementName() {
+ return elementName;
+ }
+
+ TypeMirror getElementType() {
+ return elementType;
+ }
+
+ Element getElement() {
+ return element;
+ }
+
+ @Override
+ public String toString() {
+ return "Qualified class name : " + qualifiedClassName.toString() + "\n"
+ + "Simple class name : " + simpleClassName.toString() + "\n"
+ + "Element name : " + elementName.toString() + "\n"
+ + "Element type : " + elementType.toString() + "\n";
+ }
+
+ abstract boolean isTypeValid(Elements elements, Types types);
+
+ abstract String getRandomValue();
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/processor/src/main/java/org/mozilla/gecko/annotation/processor/AnnotatedRandomInt.java
@@ -0,0 +1,34 @@
+package org.mozilla.gecko.annotation.processor;
+
+import java.util.Random;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+class AnnotatedRandomInt extends AnnotatedRandomElement {
+
+ private int minValue;
+ private int maxValue;
+
+ AnnotatedRandomInt(Element element) {
+ super(element);
+ minValue = 0;
+ maxValue = 100;
+// minValue = element.getAnnotation(RandomInt.class).minValue();
+// maxValue = element.getAnnotation(RandomInt.class).maxValue();
+ }
+
+ @Override
+ public boolean isTypeValid(Elements elements, Types types) {
+ return element.asType().getKind().equals(TypeKind.INT);
+ }
+
+ @Override
+ public String getRandomValue() {
+ Random random = new Random();
+ return "" + (minValue + random.nextInt(maxValue - minValue + 1));
+ }
+
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/processor/src/main/java/org/mozilla/gecko/annotation/processor/AnnotatedRandomString.java
@@ -0,0 +1,36 @@
+package org.mozilla.gecko.annotation.processor;
+
+import java.util.Random;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+class AnnotatedRandomString extends AnnotatedRandomElement {
+
+ private static final String QUALIFIER_STRING = "java.lang.String";
+ private static final String SEED = "ABCDEFGHJKLMNOPRSTUVYZabcdefghjklmnoprstuvyz";
+
+ AnnotatedRandomString(Element element) {
+ super(element);
+ }
+
+ @Override
+ public boolean isTypeValid(Elements elements, Types types) {
+ TypeMirror elementType = element.asType();
+ TypeMirror string = elements.getTypeElement(QUALIFIER_STRING).asType();
+ return types.isSameType(elementType, string);
+ }
+
+ @Override
+ public String getRandomValue() {
+ StringBuilder builder = new StringBuilder();
+ Random random = new Random();
+ for (int i = 0; i < 10; i++) {
+ int randIndex = random.nextInt(SEED.length());
+ builder.append(SEED.charAt(randIndex));
+ }
+ return "\"" + builder.toString() + "\"";
+ }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/processor/src/main/java/org/mozilla/gecko/annotation/processor/CustomAnnotationProcessor.java
@@ -0,0 +1,163 @@
+package org.mozilla.gecko.annotation.processor;
+
+import org.mozilla.gecko.annotation.JNITarget;
+import org.mozilla.gecko.annotation.WrapForJNI;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(Processor.class)
+public class CustomAnnotationProcessor extends AbstractProcessor {
+
+ private static final String RANDOMIZER_SUFFIX = "_Randomizer";
+ private static final String TARGET_STATEMENT_FORMAT = "target.%1$s = %2$s";
+ private static final String CONST_PARAM_TARGET_NAME = "target";
+
+ private static final char CHAR_DOT = '.';
+
+ private static final List<Class<? extends Annotation>> TYPES = Arrays.asList(
+ JNITarget.class, WrapForJNI.class);
+
+ private Messager messager;
+ private Types typesUtil;
+ private Elements elementsUtil;
+ private Filer filer;
+
+ @Override
+ public synchronized void init(ProcessingEnvironment processingEnv) {
+ super.init(processingEnv);
+ messager = processingEnv.getMessager();
+ messager.printMessage(Diagnostic.Kind.NOTE, "TEST init".toString());
+ typesUtil = processingEnv.getTypeUtils();
+ elementsUtil = processingEnv.getElementUtils();
+ filer = processingEnv.getFiler();
+ System.out.println("init");
+// throw new RuntimeException("test");
+ }
+
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ Set<String> annotations = new LinkedHashSet<>();
+ for (Class<? extends Annotation> annotation : TYPES) {
+ annotations.add(annotation.getCanonicalName());
+ }
+ return annotations;
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+// Map<String, List<AnnotatedRandomElement>> annotatedElementMap = new LinkedHashMap<>();
+//
+
+ for (Element element : roundEnv.getElementsAnnotatedWith(WrapForJNI.class)) {
+// AnnotatedRandomInt randomizerElement = new AnnotatedRandomInt(element);
+ messager.printMessage(Diagnostic.Kind.NOTE, element.getSimpleName());
+// if (!randomizerElement.isTypeValid(elementsUtil, typesUtil)) {
+// messager.printMessage(Diagnostic.Kind.ERROR, randomizerElement.getSimpleClassName().toString() + "#"
+// + randomizerElement.getElementName().toString() + " is not in valid type int");
+// }
+// addAnnotatedElement(annotatedElementMap, randomizerElement);
+ }
+//
+// for (Element element : roundEnv.getElementsAnnotatedWith(RandomString.class)) {
+// AnnotatedRandomString randomizerElement = new AnnotatedRandomString(element);
+// messager.printMessage(Diagnostic.Kind.NOTE, randomizerElement.toString());
+// if (!randomizerElement.isTypeValid(elementsUtil, typesUtil)) {
+// messager.printMessage(Diagnostic.Kind.ERROR, randomizerElement.getSimpleClassName().toString() + "#"
+// + element.getSimpleName() + " is not in valid type String");
+// }
+// addAnnotatedElement(annotatedElementMap, randomizerElement);
+// }
+//
+// if (annotatedElementMap.size() == 0) {
+// return true;
+// }
+//
+// try {
+// for (Map.Entry<String, List<AnnotatedRandomElement>> entry : annotatedElementMap.entrySet()) {
+// MethodSpec constructor = createConstructor(entry.getValue());
+// TypeSpec binder = createClass(getClassName(entry.getKey()), constructor);
+// JavaFile javaFile = JavaFile.builder(getPackage(entry.getKey()), binder).build();
+// javaFile.writeTo(filer);
+// }
+//
+// } catch (IOException e) {
+// messager.printMessage(Diagnostic.Kind.ERROR, "Error on creating java file");
+// }
+
+ return true;
+ }
+
+/*
+ private MethodSpec createConstructor(List<AnnotatedRandomElement> randomElements) {
+ AnnotatedRandomElement firstElement = randomElements.get(0);
+ MethodSpec.Builder builder = MethodSpec.constructorBuilder()
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(TypeName.get(firstElement.getElement().getEnclosingElement().asType()), CONST_PARAM_TARGET_NAME);
+ for (int i = 0; i < randomElements.size(); i++) {
+ addStatement(builder, randomElements.get(i));
+
+ }
+ return builder.build();
+ }
+
+ private void addStatement(MethodSpec.Builder builder, AnnotatedRandomElement randomElement) {
+ builder.addStatement(String.format(
+ TARGET_STATEMENT_FORMAT,
+ randomElement.getElementName().toString(),
+ randomElement.getRandomValue())
+ );
+ }
+
+ private TypeSpec createClass(String className, MethodSpec constructor) {
+ return TypeSpec.classBuilder(className + RANDOMIZER_SUFFIX)
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addMethod(constructor)
+ .build();
+ }
+
+ private String getPackage(String qualifier) {
+ return qualifier.substring(0, qualifier.lastIndexOf(CHAR_DOT));
+ }
+
+ private String getClassName(String qualifier) {
+ return qualifier.substring(qualifier.lastIndexOf(CHAR_DOT) + 1);
+ }
+
+ private void addAnnotatedElement(Map<String, List<AnnotatedRandomElement>> map, AnnotatedRandomElement randomElement) {
+ String qualifier = randomElement.getQualifiedClassName().toString();
+ if (map.get(qualifier) == null) {
+ map.put(qualifier, new ArrayList<AnnotatedRandomElement>());
+ }
+ map.get(qualifier).add(randomElement);
+ }
+*/
+}
--- a/settings.gradle
+++ b/settings.gradle
@@ -9,40 +9,45 @@ proc.consumeProcessOutput(standardOutput
proc.waitFor()
// Only show the output if something went wrong.
if (proc.exitValue() != 0) {
throw new GradleException("Process '${commandLine}' finished with non-zero exit value ${proc.exitValue()}:\n\n${standardOutput.toString()}")
}
import groovy.json.JsonSlurper
+
def slurper = new JsonSlurper()
def json = slurper.parseText(standardOutput.toString())
if (json.substs.MOZ_BUILD_APP != 'mobile/android') {
throw new GradleException("Building with Gradle is only supported for Fennec, i.e., MOZ_BUILD_APP == 'mobile/android'.")
}
// Set the Android SDK location. This is the *least specific* mechanism, which
// is unfortunate: we'd prefer to use the *most specific* mechanism. That is,
// local.properties (first 'sdk.dir', then 'android.dir') and then the
// environment variable ANDROID_HOME will override this. That's unfortunate,
// but it's hard to automatically arrange better.
System.setProperty('android.home', json.substs.ANDROID_SDK_ROOT)
+include ':annotations'
include ':app'
include ':geckoview'
include ':geckoview_example'
include ':omnijar'
+include ':processor'
include ':thirdparty'
+project(':annotations').projectDir = new File("${json.topsrcdir}/mobile/android/annotations")
project(':app').projectDir = new File("${json.topsrcdir}/mobile/android/app")
project(':geckoview').projectDir = new File("${json.topsrcdir}/mobile/android/geckoview")
project(':geckoview_example').projectDir = new File("${json.topsrcdir}/mobile/android/geckoview_example")
project(':omnijar').projectDir = new File("${json.topsrcdir}/mobile/android/app/omnijar")
+project(':processor').projectDir = new File("${json.topsrcdir}/mobile/android/processor")
project(':thirdparty').projectDir = new File("${json.topsrcdir}/mobile/android/thirdparty")
if (json.substs.MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER) {
include ':bouncer'
project(':bouncer').projectDir = new File("${json.topsrcdir}/mobile/android/bouncer")
}
// The Gradle instance is shared between settings.gradle and all the