summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2013-06-14 04:40:39 +0200
committerSven Gothel <[email protected]>2013-06-14 04:40:39 +0200
commita7802a2ab90a68ecbba962149a335f975fce24e7 (patch)
treea702cdf0e35e36f6e2bf042b6325c3f435c5b65c /src
parent3cf28f2980b1593e4b32230d5206decd6afe2cbb (diff)
GlueGen (Compile Time): Add 'CStruct' Annotation Processor (APT) to 'gluegen' C Structs on-the-fly (2-pass actually).
Convenient annotation processing (APT) hooked to 'javac' (1.6) via gluegen.jar META-INF service provider 'javax.annotation.processing.Processor' -> 'com.jogamp.gluegen.structgen.CStructAnnotationProcessor'. Am implicit APT / JAVAC would be possible, however - to have a proper process the unit test utilizes an explicit 2 pass run: <!-- Annotation Processor Only - First --> <javac destdir="${build_t.java}"> <classpath refid="junit.compile.classpath"/> <compilerarg value="-proc:only"/> <compilerarg value="-J-Djogamp.gluegen.structgen.debug"/> <compilerarg value="-J-Djogamp.gluegen.structgen.output=${build_t.gen}/classes"/> <src path="${test.base.dir}/com/jogamp/gluegen/test/junit/structgen"/> </javac> <!-- Javac Only - Second --> <javac destdir="${build_t.java}"> <classpath refid="junit.compile.classpath"/> <compilerarg value="-proc:none"/> <src path="${test.base.dir}"/> <src path="${build_t.gen}" /> </javac> Original code from Michael Bien's 'superglue' git://github.com/mbien/superglue.git, finally merged to GlueGen (as once intended). Note: The APT javac pass requires to use 'gluegen.jar' instead of 'gluegen-rt.jar' ! The 2-pass process also alows using the runtime gluegen-rt.jar and hence ensures clean namespace check at compilation.
Diffstat (limited to 'src')
-rw-r--r--src/java/com/jogamp/common/util/PropertyAccess.java11
-rw-r--r--src/java/com/jogamp/gluegen/JavaEmitter.java18
-rw-r--r--src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java2
-rw-r--r--src/java/com/jogamp/gluegen/structgen/CStruct.java53
-rw-r--r--src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java240
-rw-r--r--src/junit/com/jogamp/gluegen/test/junit/structgen/TestStruct01.h24
-rw-r--r--src/junit/com/jogamp/gluegen/test/junit/structgen/TestStructGen01.java51
7 files changed, 392 insertions, 7 deletions
diff --git a/src/java/com/jogamp/common/util/PropertyAccess.java b/src/java/com/jogamp/common/util/PropertyAccess.java
index dde6b50..1a4bc7e 100644
--- a/src/java/com/jogamp/common/util/PropertyAccess.java
+++ b/src/java/com/jogamp/common/util/PropertyAccess.java
@@ -156,6 +156,17 @@ public class PropertyAccess {
return s;
}
+ /** See {@link #getProperty(String, boolean)}, additionally allows a <code>defaultValue</code> if property value is <code>null</code>. */
+ public static final String getProperty(final String propertyKey, final boolean jnlpAlias, String defaultValue)
+ throws SecurityException, NullPointerException, IllegalArgumentException {
+ final String s = PropertyAccess.getProperty(propertyKey, jnlpAlias);
+ if( null != s ) {
+ return s;
+ } else {
+ return defaultValue;
+ }
+ }
+
private static final String getTrustedPropKey(final String propertyKey) {
return AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java
index 1a6c313..18b2636 100644
--- a/src/java/com/jogamp/gluegen/JavaEmitter.java
+++ b/src/java/com/jogamp/gluegen/JavaEmitter.java
@@ -811,14 +811,14 @@ public class JavaEmitter implements GlueEmitter {
writer = openFile(
cfg.javaOutputDir() + File.separator +
CodeGenUtils.packageAsPath(structClassPkg) +
- File.separator + containingTypeName + ".java");
+ File.separator + containingTypeName + ".java", containingTypeName);
CodeGenUtils.emitAutogeneratedWarning(writer, this);
if (needsNativeCode) {
String nRoot = cfg.nativeOutputDir();
if (cfg.nativeOutputUsesJavaHierarchy()) {
nRoot += File.separator + CodeGenUtils.packageAsPath(cfg.packageName());
}
- newWriter = openFile(nRoot + File.separator + containingTypeName + "_JNI.c");
+ newWriter = openFile(nRoot + File.separator + containingTypeName + "_JNI.c", containingTypeName);
CodeGenUtils.emitAutogeneratedWarning(newWriter, this);
emitCHeader(newWriter, containingTypeName);
}
@@ -1379,7 +1379,13 @@ public class JavaEmitter implements GlueEmitter {
return layout;
}
- protected PrintWriter openFile(String filename) throws IOException {
+ /**
+ * @param filename the class's full filename to open w/ write access
+ * @param simpleClassName the simple class name, i.e. w/o package name
+ * @return a {@link PrintWriter} instance to write the class source file
+ * @throws IOException
+ */
+ protected PrintWriter openFile(String filename, String simpleClassName) throws IOException {
//System.out.println("Trying to open: " + filename);
File file = new File(filename);
String parentDir = file.getParent();
@@ -1432,13 +1438,13 @@ public class JavaEmitter implements GlueEmitter {
}
if (cfg.allStatic() || cfg.emitInterface()) {
- javaWriter = openFile(jRoot + File.separator + cfg.className() + ".java");
+ javaWriter = openFile(jRoot + File.separator + cfg.className() + ".java", cfg.className());
}
if (!cfg.allStatic() && cfg.emitImpl()) {
- javaImplWriter = openFile(jImplRoot + File.separator + cfg.implClassName() + ".java");
+ javaImplWriter = openFile(jImplRoot + File.separator + cfg.implClassName() + ".java", cfg.implClassName());
}
if (cfg.emitImpl()) {
- cWriter = openFile(nRoot + File.separator + cfg.implClassName() + "_JNI.c");
+ cWriter = openFile(nRoot + File.separator + cfg.implClassName() + "_JNI.c", cfg.implClassName());
}
if (javaWriter != null) {
diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java
index ba884d0..82434cf 100644
--- a/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java
+++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java
@@ -285,7 +285,7 @@ public class ProcAddressEmitter extends JavaEmitter {
}
String jImplRoot = getJavaOutputDir() + File.separator + CodeGenUtils.packageAsPath(implPackageName);
- tableWriter = openFile(jImplRoot + File.separator + tableClassName + ".java");
+ tableWriter = openFile(jImplRoot + File.separator + tableClassName + ".java", tableClassName);
emittedTableEntries = new HashSet<String>();
CodeGenUtils.emitAutogeneratedWarning(tableWriter, this);
diff --git a/src/java/com/jogamp/gluegen/structgen/CStruct.java b/src/java/com/jogamp/gluegen/structgen/CStruct.java
new file mode 100644
index 0000000..9d57196
--- /dev/null
+++ b/src/java/com/jogamp/gluegen/structgen/CStruct.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010, Michael Bien. All rights reserved.
+ * Copyright (c) 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Michael Bien nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Michael Bien BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jogamp.gluegen.structgen;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author Michael Bien
+ * @author Sven Gothel, et.al.
+ */
+@Target(value = {ElementType.FIELD, ElementType.LOCAL_VARIABLE})
+@Retention(value = RetentionPolicy.SOURCE)
+public @interface CStruct {
+
+ /**
+ * Relative path to the header file.
+ */
+ String header();
+
+ /**
+ * The name of the struct.
+ */
+ String name() default "_default_";
+}
diff --git a/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java b/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java
new file mode 100644
index 0000000..fef8545
--- /dev/null
+++ b/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2010, Michael Bien. All rights reserved.
+ * Copyright (c) 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Michael Bien nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Michael Bien BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jogamp.gluegen.structgen;
+
+import com.jogamp.common.util.PropertyAccess;
+import com.jogamp.gluegen.GlueGen;
+import com.jogamp.gluegen.JavaEmitter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashSet;
+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.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+import javax.tools.Diagnostic.Kind;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+
+import jogamp.common.Debug;
+
+import static java.io.File.*;
+
+/**
+ * <p>
+ * If the <i>header file</i> is absolute, the <i>root path</i> is the parent folder of the folder containing the package source, i.e.:
+ * <pre>
+ * Header: /gluegen/src/junit/com/jogamp/test/structgen/TestStruct01.h
+ * Root: /gluegen/src/junit/..
+ * Root: /gluegen/src
+ * </pre>
+ * Otherwise the <i>user.dir</i> is being used as the <i>root path</i>
+ * and the relative <i>header file</i> is appended to it.
+ * </p>
+ * The property <code>jogamp.gluegen.structgen.output</code> allows setting a default <i>outputPath</i>
+ * for the generated sources, if the {@link ProcessingEnvironment}'s <code>structgen.output</code> option is not set.
+ * <p>
+ * If the <i>outputPath</i> is relative, it is appended to the <i>root path</i>,
+ * otherwise it is taken as-is.
+ * </p>
+ * <p>
+ * User can enable DEBUG while defining property <code>jogamp.gluegen.structgen.debug</code>.
+ * </p>
+ *
+ * @author Michael Bien
+ * @author Sven Gothel, et.al.
+ */
+@SupportedAnnotationTypes(value = {"com.jogamp.gluegen.structgen.CStruct"})
+@SupportedSourceVersion(SourceVersion.RELEASE_6)
+public class CStructAnnotationProcessor extends AbstractProcessor {
+ private static final boolean DEBUG = Debug.isPropertyDefined("jogamp.gluegen.structgen.debug", true);
+ private static final String DEFAULT = "_default_";
+
+ private static final String STRUCTGENOUTPUT_OPTION = "structgen.output";
+ private static final String STRUCTGENOUTPUT = PropertyAccess.getProperty("jogamp.gluegen."+STRUCTGENOUTPUT_OPTION, true, "gensrc");
+
+ private Filer filer;
+ private Messager messager;
+ private Elements eltUtils;
+ private String outputPath;
+
+ private final static Set<String> generatedStructs = new HashSet<String>();
+
+
+ @Override
+ public void init(ProcessingEnvironment processingEnv) {
+ super.init(processingEnv);
+
+ filer = processingEnv.getFiler();
+ messager = processingEnv.getMessager();
+ eltUtils = processingEnv.getElementUtils();
+
+ outputPath = processingEnv.getOptions().get(STRUCTGENOUTPUT_OPTION);
+ outputPath = outputPath == null ? STRUCTGENOUTPUT : outputPath;
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
+
+ Set<? extends Element> elements = env.getElementsAnnotatedWith(CStruct.class);
+
+ for (Element element : elements) {
+
+ final String packageName = eltUtils.getPackageOf(element).toString();
+ final String user_dir = System.getProperty("user.dir");
+
+ try {
+ final CStruct struct = element.getAnnotation(CStruct.class);
+ final String headerRelPath = struct.header();
+ final FileObject header;
+ final File headerFileRel;
+ {
+ FileObject h = filer.getResource(StandardLocation.SOURCE_PATH, packageName, headerRelPath);
+ File f = new File( h.toUri().getPath() ); // URI is incomplete (no scheme), hence use path only!
+ if( !f.exists() ) {
+ h = filer.getResource(StandardLocation.SOURCE_PATH, "", headerRelPath);
+ f = new File( h.toUri().getPath() ); // URI is incomplete (no scheme), hence use path only!
+ if( !f.exists() ) {
+ // bail out
+ throw new RuntimeException("Could not locate header "+headerRelPath+", package "+packageName);
+ }
+ }
+ header = h;
+ headerFileRel = f;
+ }
+
+ final boolean headerFileRelIsAbsolute = headerFileRel.isAbsolute();
+
+ final String root;
+ final File headerFile;
+ if( headerFileRelIsAbsolute ) {
+ headerFile = headerFileRel;
+
+ String root0 = headerFile.getAbsolutePath();
+ root0 = root0.substring(0, root0.length()-headerFile.getName().length()-1);
+ root = root0.substring(0, root0.length()-packageName.length()) +"..";
+ } else {
+ root = user_dir;
+ headerFile = new File(root + separator + header.toUri());
+ }
+ System.err.println("CStruct: "+struct);
+ System.err.println("CStruct: package "+packageName+", header "+headerRelPath+": "+headerFile);
+ if(DEBUG) {
+ System.err.println("CStruct: header "+headerFileRel+", abs-path: "+headerFileRelIsAbsolute);
+ System.err.println("CStruct: Root: "+root);
+ System.err.println("CStruct: user.dir: "+user_dir);
+ }
+
+ generateStructBinding(element, struct, root, packageName, headerFile);
+ } catch (IOException ex) {
+ throw new RuntimeException("IOException while processing!", ex);
+ }
+ }
+ return true;
+ }
+
+ private void generateStructBinding(Element element, CStruct struct, String root, String pakage, File header) throws IOException {
+ final String declaredType = element.asType().toString();
+ final String structName = struct.name().equals(DEFAULT) ? declaredType : struct.name();
+
+ if( generatedStructs.contains(structName) ) {
+ messager.printMessage(Kind.WARNING, "struct "+structName+" already defined elsewhere.", element);
+ return;
+ }
+ System.out.println("generating struct accessor for struct: "+structName);
+
+ generatedStructs.add(structName);
+
+ final boolean outputDirAbs;
+ {
+ final File outputDirFile = new File(outputPath);
+ outputDirAbs = outputDirFile.isAbsolute();
+ }
+ final String outputPath1 = outputDirAbs ? outputPath : root + separator + outputPath;
+ final String config = outputPath1 + separator + header.getName() + ".cfg";
+ final File configFile = new File(config);
+ if(DEBUG) {
+ System.err.println("CStruct: OutputDir: "+outputPath+", is-abs "+outputDirAbs);
+ System.err.println("CStruct: OutputPath: "+outputPath1);
+ System.err.println("CStruct: ConfigFile: "+configFile);
+ }
+
+ FileWriter writer = null;
+ try{
+ writer = new FileWriter(configFile);
+ writer.write("Package "+pakage+"\n");
+ writer.write("EmitStruct "+structName+"\n");
+ if(!struct.name().equals(DEFAULT)) {
+ writer.write("RenameJavaType " + struct.name()+" " + declaredType +"\n");
+ }
+ } finally {
+ if( null != writer ) {
+ writer.close();
+ }
+ }
+
+ // TODO: Handle exceptions .. suppressed by Gluegen.main(..) ?
+ GlueGen.main(
+ // "-I"+path+"/build/",
+ "-O" + outputPath1,
+ "-E" + AnnotationProcessorJavaStructEmitter.class.getName(),
+ "-C" + config,
+ header.getPath());
+
+ configFile.delete();
+ }
+
+ public static class AnnotationProcessorJavaStructEmitter extends JavaEmitter {
+
+ @Override
+ protected PrintWriter openFile(String filename, String simpleClassName) throws IOException {
+
+ // look for recursive generated structs... keep it DRY
+ if( !simpleClassName.endsWith("32") &&
+ !simpleClassName.endsWith("64") ) {
+
+ System.out.println("generating -> " + simpleClassName);
+ generatedStructs.add(simpleClassName);
+ }
+
+ return super.openFile(filename, simpleClassName);
+ }
+
+ }
+
+}
diff --git a/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStruct01.h b/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStruct01.h
new file mode 100644
index 0000000..6ad77d9
--- /dev/null
+++ b/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStruct01.h
@@ -0,0 +1,24 @@
+//
+// TestStruct01.h
+//
+
+typedef struct {
+ float x, y, z;
+} Vec3f;
+
+typedef struct {
+ Vec3f orig, dir;
+} Camera;
+
+typedef struct {
+ unsigned int width, height;
+ int superSamplingSize;
+ int actvateFastRendering;
+ int enableShadow;
+
+ unsigned int maxIterations;
+ float epsilon;
+ float mu[4];
+ float light[3];
+ Camera camera;
+} RenderingConfig;
diff --git a/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStructGen01.java b/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStructGen01.java
new file mode 100644
index 0000000..444593d
--- /dev/null
+++ b/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStructGen01.java
@@ -0,0 +1,51 @@
+package com.jogamp.gluegen.test.junit.structgen;
+
+import com.jogamp.gluegen.structgen.CStruct;
+import com.jogamp.junit.util.JunitTracer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestStructGen01 extends JunitTracer {
+
+ // APT is only triggered for fields,
+ // hence we use unused fields in this unit test!
+
+ // @CStruct(name="RenderingConfig", header="TestStruct01.h")
+ // MyRenderingConfig config;
+
+ @CStruct(header="TestStruct01.h")
+ RenderingConfig config0;
+
+ @Test
+ public void test01() {
+ RenderingConfig config = RenderingConfig.create();
+ Vec3f dir = config.getCamera().getDir();
+ dir.setX(0.5f);
+ dir.setY(0.6f);
+ dir.setZ(0.7f);
+ Vec3f dir2 = Vec3f.create(dir.getBuffer());
+ Assert.assertEquals(dir.getX(), dir2.getX(), 0.0001f);
+ Assert.assertEquals(dir.getY(), dir2.getY(), 0.0001f);
+ Assert.assertEquals(dir.getZ(), dir2.getZ(), 0.0001f);
+ }
+
+ @Test
+ public void test02() {
+ Camera cam = Camera.create();
+ Vec3f cam_dir = cam.getDir();
+ Vec3f cam_orig = cam.getOrig();
+ cam_dir.setX(1);
+ cam_dir.setY(2);
+ cam_dir.setZ(3);
+ cam_orig.setX(0);
+ cam_orig.setY(1);
+ cam_orig.setZ(2);
+ }
+
+ public static void main(String args[]) {
+ String tstname = TestStructGen01.class.getName();
+ org.junit.runner.JUnitCore.main(tstname);
+ }
+
+}