Bug 1378410 - 1. Support BuildFlag annotation for generated bindings; r=snorp

Add a BuildFlag annotation, which when specified for classes, will wrap
generated code in `#ifdef` or `#ifndef` blocks. This functionality is
used for conditionally excluding generated code when NIghtly becomes
Beta, without the need to regenerate bindings.

MozReview-Commit-ID: L2NFM8CHKqF

--HG--
extra : rebase_source : 6ebc82d11fd1aa4aeb57a46262e678480d23de83
This commit is contained in:
Jim Chen 2017-09-01 14:02:29 -04:00
parent b0873633b7
commit f0ca31be3a
9 changed files with 94 additions and 9 deletions

View File

@ -29,7 +29,7 @@ public class AnnotationProcessor {
public static void main(String[] args) {
// We expect a list of jars on the commandline. If missing, whinge about it.
if (args.length <= 2) {
if (args.length < 2) {
System.err.println("Usage: java AnnotationProcessor outprefix jarfiles ...");
System.exit(1);
}
@ -56,15 +56,19 @@ public class AnnotationProcessor {
"#ifndef " + getHeaderGuardName(HEADER_FILE) + "\n" +
"#define " + getHeaderGuardName(HEADER_FILE) + "\n" +
"\n" +
"#ifndef MOZ_PREPROCESSOR\n" +
"#include \"mozilla/jni/Refs.h\"\n" +
"#endif\n" +
"\n" +
"namespace mozilla {\n" +
"namespace java {\n" +
"\n");
implementationFile.append(
"#ifndef MOZ_PREPROCESSOR\n" +
"#include \"" + HEADER_FILE + "\"\n" +
"#include \"mozilla/jni/Accessors.h\"\n" +
"#endif\n" +
"\n" +
"namespace mozilla {\n" +
"namespace java {\n" +
@ -74,8 +78,10 @@ public class AnnotationProcessor {
"#ifndef " + getHeaderGuardName(NATIVES_FILE) + "\n" +
"#define " + getHeaderGuardName(NATIVES_FILE) + "\n" +
"\n" +
"#ifndef MOZ_PREPROCESSOR\n" +
"#include \"" + HEADER_FILE + "\"\n" +
"#include \"mozilla/jni/Natives.h\"\n" +
"#endif\n" +
"\n" +
"namespace mozilla {\n" +
"namespace java {\n" +

View File

@ -27,6 +27,7 @@ public class CodeGenerator {
private final Class<?> cls;
private final String clsName;
private final ClassWithOptions options;
private AnnotationInfo.CallingThread callingThread = null;
private int numNativesInits;
@ -35,9 +36,11 @@ public class CodeGenerator {
public CodeGenerator(ClassWithOptions annotatedClass) {
this.cls = annotatedClass.wrappedClass;
this.clsName = annotatedClass.generatedName;
this.options = annotatedClass;
final String unqualifiedName = Utils.getUnqualifiedName(clsName);
header.append(
Utils.getIfdefHeader(annotatedClass.ifdef) +
"class " + clsName + " : public mozilla::jni::ObjectBase<" +
unqualifiedName + ">\n" +
"{\n" +
@ -49,11 +52,13 @@ public class CodeGenerator {
"\n");
cpp.append(
Utils.getIfdefHeader(annotatedClass.ifdef) +
"const char " + clsName + "::name[] =\n" +
" \"" + cls.getName().replace('.', '/') + "\";\n" +
"\n");
natives.append(
Utils.getIfdefHeader(annotatedClass.ifdef) +
"template<class Impl>\n" +
"class " + clsName + "::Natives : " +
"public mozilla::jni::NativeImpl<" + unqualifiedName + ", Impl>\n" +
@ -556,6 +561,8 @@ public class CodeGenerator {
* @return The bytes to be written to the wrappers file.
*/
public String getWrapperFileContents() {
cpp.append(
Utils.getIfdefFooter(options.ifdef));
return cpp.toString();
}
@ -580,7 +587,8 @@ public class CodeGenerator {
}
header.append(
"};\n" +
"\n");
"\n" +
Utils.getIfdefFooter(options.ifdef));
return header.toString();
}
@ -600,7 +608,8 @@ public class CodeGenerator {
"template<class Impl>\n" +
"const JNINativeMethod " + clsName + "::Natives<Impl>::methods[] = {" + nativesInits + '\n' +
"};\n" +
"\n");
"\n" +
Utils.getIfdefFooter(options.ifdef));
return natives.toString();
}
}

View File

@ -412,7 +412,8 @@ public class SDKProcessor {
StringBuilder headerFile) throws ParseException {
String generatedName = clazz.getSimpleName();
CodeGenerator generator = new CodeGenerator(new ClassWithOptions(clazz, generatedName));
CodeGenerator generator = new CodeGenerator(
new ClassWithOptions(clazz, generatedName, /* ifdef */ ""));
generateMembers(generator, clsInfo,
sortAndFilterMembers(clazz, clazz.getConstructors()));

View File

@ -7,9 +7,11 @@ package org.mozilla.gecko.annotationProcessors.classloader;
public class ClassWithOptions {
public final Class<?> wrappedClass;
public final String generatedName;
public final String ifdef;
public ClassWithOptions(Class<?> someClass, String name) {
public ClassWithOptions(Class<?> someClass, String name, String ifdef) {
wrappedClass = someClass;
generatedName = name;
this.ifdef = ifdef;
}
}

View File

@ -4,6 +4,8 @@
package org.mozilla.gecko.annotationProcessors.classloader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Iterator;
/**
@ -67,7 +69,27 @@ public class JarClassIterator implements Iterator<ClassWithOptions> {
return fillLookAheadIfPossible();
}
lookAhead = new ClassWithOptions(ret, ret.getSimpleName());
String ifdef = "";
for (final Annotation annotation : ret.getDeclaredAnnotations()) {
Class<? extends Annotation> annotationType = annotation.annotationType();
if (!annotationType.getName().equals(
"org.mozilla.gecko.annotation.BuildFlag")) {
continue;
}
try {
final Method valueMethod = annotationType.getDeclaredMethod("value");
valueMethod.setAccessible(true);
ifdef = (String) valueMethod.invoke(annotation);
break;
} catch (final Exception e) {
System.err.println("Unable to read BuildFlag annotation.");
e.printStackTrace(System.err);
System.exit(1);
}
}
lookAhead = new ClassWithOptions(ret, ret.getSimpleName(), ifdef);
return true;
} catch (ClassNotFoundException e) {
System.err.println("Unable to enumerate class: " + className + ". Corrupted jar file?");

View File

@ -77,8 +77,8 @@ public class GeneratableElementIterator implements Iterator<AnnotatableEntity> {
int count = 0;
for (int i = 0; i < candidates.length; ++i) {
final GeneratableElementIterator testIterator
= new GeneratableElementIterator(new ClassWithOptions(candidates[i], null));
final GeneratableElementIterator testIterator = new GeneratableElementIterator(
new ClassWithOptions(candidates[i], null, /* ifdef */ ""));
if (testIterator.hasNext()
|| testIterator.getFilteredInnerClasses() != null) {
count++;
@ -108,7 +108,9 @@ public class GeneratableElementIterator implements Iterator<AnnotatableEntity> {
for (Class<?> candidate : candidates) {
if (candidate != null) {
ret[count++] = new ClassWithOptions(
candidate, mClass.generatedName + "::" + candidate.getSimpleName());
candidate,
mClass.generatedName + "::" + candidate.getSimpleName(),
/* ifdef */ "");
}
}
assert ret.length == count;

View File

@ -327,4 +327,20 @@ public class Utils {
return null;
}
}
public static String getIfdefHeader(String ifdef) {
if (ifdef.isEmpty()) {
return "";
} else if (ifdef.startsWith("!")) {
return "#ifndef " + ifdef.substring(1) + "\n";
}
return "#ifdef " + ifdef + "\n";
}
public static String getIfdefFooter(String ifdef) {
if (ifdef.isEmpty()) {
return "";
}
return "#endif // " + ifdef + "\n";
}
}

View File

@ -148,6 +148,7 @@ 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 [
'annotation/BuildFlag.java',
'annotation/JNITarget.java',
'annotation/ReflectionTarget.java',
'annotation/RobocopTarget.java',

View File

@ -0,0 +1,26 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation is used to tag classes that are conditionally built behind
* build flags. Any generated JNI bindings will incorporate the specified build
* flags.
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface BuildFlag {
/**
* Preprocessor macro for conditionally building the generated bindings.
* "MOZ_FOO" wraps generated bindings in "#ifdef MOZ_FOO / #endif"
* "!MOZ_FOO" wraps generated bindings in "#ifndef MOZ_FOO / #endif"
*/
String value() default "";
}