diff options
-rw-r--r-- | make/build-test.xml | 19 | ||||
-rw-r--r-- | make/build.xml | 3 | ||||
-rwxr-xr-x | make/scripts/runtest.sh | 3 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/PropertyAccess.java | 11 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaEmitter.java | 18 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java | 2 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/structgen/CStruct.java | 53 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java | 240 | ||||
-rw-r--r-- | src/junit/com/jogamp/gluegen/test/junit/structgen/TestStruct01.h | 24 | ||||
-rw-r--r-- | src/junit/com/jogamp/gluegen/test/junit/structgen/TestStructGen01.java | 51 |
10 files changed, 416 insertions, 8 deletions
diff --git a/make/build-test.xml b/make/build-test.xml index 3c7da12..31dc773 100644 --- a/make/build-test.xml +++ b/make/build-test.xml @@ -114,6 +114,7 @@ <echo message=" test.base.dir ${test.base.dir}"/> <echo message=" build_t.gen ${build_t.gen}"/> + <!-- Annotation Processor Only - First --> <javac destdir="${build_t.java}" fork="yes" includeAntRuntime="false" @@ -124,6 +125,24 @@ bootclasspath="${target.rt.jar}" debug="${javacdebug}" debuglevel="${javacdebuglevel}"> <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}" + fork="yes" + includeAntRuntime="false" + memoryMaximumSize="${javac.memorymax}" + encoding="UTF-8" + source="${target.sourcelevel}" + target="${target.targetlevel}" + bootclasspath="${target.rt.jar}" + debug="${javacdebug}" debuglevel="${javacdebuglevel}"> + <classpath refid="junit.compile.classpath"/> + <compilerarg value="-proc:none"/> <src path="${test.base.dir}"/> <src path="${build_t.gen}" /> </javac> diff --git a/make/build.xml b/make/build.xml index d8fd553..90151c6 100644 --- a/make/build.xml +++ b/make/build.xml @@ -802,6 +802,9 @@ <!-- Build gluegen.jar. --> <jar destfile="${build}/gluegen.jar" manifest="${build}/Manifest.temp"> + <service type="javax.annotation.processing.Processor"> + <provider classname="com.jogamp.gluegen.structgen.CStructAnnotationProcessor"/> + </service> <fileset dir="${classes}"> <include name="**/*.class" /> <exclude name="${jogamp-android-launcher.classes}" /> diff --git a/make/scripts/runtest.sh b/make/scripts/runtest.sh index 165f64d..65b3573 100755 --- a/make/scripts/runtest.sh +++ b/make/scripts/runtest.sh @@ -96,6 +96,7 @@ function onetest() { #onetest com.jogamp.common.os.TestElfReader01 2>&1 | tee -a $LOG #onetest com.jogamp.gluegen.test.junit.generation.Test1p1JavaEmitter 2>&1 | tee -a $LOG #onetest com.jogamp.gluegen.test.junit.generation.Test1p2ProcAddressEmitter 2>&1 | tee -a $LOG +onetest com.jogamp.gluegen.test.junit.structgen.TestStructGen01 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.TestPlatform01 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.TestRunnableTask01 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.TestIOUtil01 2>&1 | tee -a $LOG @@ -105,4 +106,4 @@ function onetest() { #onetest com.jogamp.common.net.AssetURLConnectionUnregisteredTest 2>&1 | tee -a $LOG #onetest com.jogamp.common.net.AssetURLConnectionRegisteredTest 2>&1 | tee -a $LOG #onetest com.jogamp.common.net.URLCompositionTest 2>&1 | tee -a $LOG -onetest com.jogamp.junit.sec.TestSecIOUtil01 2>&1 | tee -a $LOG +#onetest com.jogamp.junit.sec.TestSecIOUtil01 2>&1 | tee -a $LOG 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); + } + +} |