From 1e53a38eb06aa8ff50660fdf7bd8570b27f27c56 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Sun, 11 May 2014 03:03:36 +0200
Subject: Bug 923: Remove dependency of CStruct annotation processor
 _generation_ and generated_class_user due to Java8 issues.

Java8's annotation processor as embedded within javac does not allow
referencing not-yet existing generated classes in a class source code
which will produce these generated classes via the annotation process.

Example:

+++
import com.jogamp.gluegen.structgen.CStruct;

public class Lala {
  @CStruct(name="RenderingConfig", header="TestStruct01.h")
  public RenderingConfig config;
}
+++

Above example illustrates that the type 'RenderingConfig'
does not exist at time of processing the annotation.
The type will be created via the annotation process itself.

Even though we pass '-proc:only', i.e. skip java compilation,
Java8's annotation processing via javac fails in such case.

We see this as a bug within javac's annotation processing itself!

+++

This workaround splits the annotation process and using the class as
generated by the former.

To allow this to work, CStruct receives a new field 'jname'
allowing to enforce the java-name of the structure
using a dummy field type like boolean.

  @CStruct(name="RenderingConfig", jname="RenderingConfig", header="TestStruct01.h")
  public boolean dummy;

Further more CStruct can be annotated on the package, i.e. 'package-info.java',
avoiding the dependency problem altogether.
To support multiple header files and types,
'CStructs' has been introduced using an array of 'CStruct'.

  @CStructs({@CStruct(name="RenderingConfig", header="TestStruct01.h"), @CStruct(name="Pixel", header="TestStruct02.h")})
  package com.jogamp.gluegen.test.junit.structgen;

Tests:
  - Build w/ Java7 and Java8
  - Validated 'major version 50' (Java 6) class files (OK)
---
 src/java/com/jogamp/gluegen/JavaEmitter.java       | 233 +++++++++++----------
 src/java/com/jogamp/gluegen/structgen/CStruct.java |  19 +-
 .../structgen/CStructAnnotationProcessor.java      | 160 +++++++++-----
 .../com/jogamp/gluegen/structgen/CStructs.java     |  46 ++++
 .../test/junit/structgen/BuildStruct01.java        |  34 +++
 .../gluegen/test/junit/structgen/TestStruct02.h    |  14 ++
 .../test/junit/structgen/TestStructGen01.java      |  23 +-
 .../test/junit/structgen/TestStructGen02.java      |  51 +++++
 .../gluegen/test/junit/structgen/package-info.java |  15 ++
 9 files changed, 405 insertions(+), 190 deletions(-)
 create mode 100644 src/java/com/jogamp/gluegen/structgen/CStructs.java
 create mode 100644 src/junit/com/jogamp/gluegen/test/junit/structgen/BuildStruct01.java
 create mode 100644 src/junit/com/jogamp/gluegen/test/junit/structgen/TestStruct02.h
 create mode 100644 src/junit/com/jogamp/gluegen/test/junit/structgen/TestStructGen02.java
 create mode 100644 src/junit/com/jogamp/gluegen/test/junit/structgen/package-info.java

(limited to 'src')

diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java
index ec8757c..3f4fa9e 100644
--- a/src/java/com/jogamp/gluegen/JavaEmitter.java
+++ b/src/java/com/jogamp/gluegen/JavaEmitter.java
@@ -836,69 +836,74 @@ public class JavaEmitter implements GlueEmitter {
       }
     }
 
-    String structClassPkg = cfg.packageForStruct(name);
-    PrintWriter writer = null;
-    PrintWriter newWriter = null;
+    final String structClassPkg = cfg.packageForStruct(name);
+    final PrintWriter javaWriter;
+    final PrintWriter jniWriter;
     try  {
-      writer = openFile(
-        cfg.javaOutputDir() + File.separator +
-        CodeGenUtils.packageAsPath(structClassPkg) +
-        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());
+        javaWriter = openFile(cfg.javaOutputDir() + File.separator +
+                              CodeGenUtils.packageAsPath(structClassPkg) +
+                              File.separator + containingTypeName + ".java", containingTypeName);
+        if( null == javaWriter ) {
+            // suppress output if openFile deliberately returns null.
+            return;
+        }
+        CodeGenUtils.emitAutogeneratedWarning(javaWriter, this);
+        if (needsNativeCode) {
+            String nRoot = cfg.nativeOutputDir();
+            if (cfg.nativeOutputUsesJavaHierarchy()) {
+                nRoot += File.separator + CodeGenUtils.packageAsPath(cfg.packageName());
+            }
+            jniWriter = openFile(nRoot + File.separator + containingTypeName + "_JNI.c", containingTypeName);
+            CodeGenUtils.emitAutogeneratedWarning(jniWriter, this);
+            emitCHeader(jniWriter, containingTypeName);
+        } else {
+            jniWriter = null;
         }
-        newWriter = openFile(nRoot + File.separator + containingTypeName + "_JNI.c", containingTypeName);
-        CodeGenUtils.emitAutogeneratedWarning(newWriter, this);
-        emitCHeader(newWriter, containingTypeName);
-      }
     } catch(Exception e)   {
-      throw new RuntimeException("Unable to open files for emission of struct class", e);
+        throw new RuntimeException("Unable to open files for emission of struct class", e);
     }
 
-    writer.println();
-    writer.println("package " + structClassPkg + ";");
-    writer.println();
-    writer.println("import java.nio.*;");
-    writer.println();
+    javaWriter.println();
+    javaWriter.println("package " + structClassPkg + ";");
+    javaWriter.println();
+    javaWriter.println("import java.nio.*;");
+    javaWriter.println();
 
-    writer.println("import " + cfg.gluegenRuntimePackage() + ".*;");
-    writer.println("import " + DynamicLookupHelper.class.getPackage().getName() + ".*;");
-    writer.println("import " + Buffers.class.getPackage().getName() + ".*;");
-    writer.println("import " + MachineDescriptionRuntime.class.getName() + ";");
-    writer.println();
+    javaWriter.println("import " + cfg.gluegenRuntimePackage() + ".*;");
+    javaWriter.println("import " + DynamicLookupHelper.class.getPackage().getName() + ".*;");
+    javaWriter.println("import " + Buffers.class.getPackage().getName() + ".*;");
+    javaWriter.println("import " + MachineDescriptionRuntime.class.getName() + ";");
+    javaWriter.println();
     List<String> imports = cfg.imports();
     for (String str : imports) {
-      writer.print("import ");
-      writer.print(str);
-      writer.println(";");
+      javaWriter.print("import ");
+      javaWriter.print(str);
+      javaWriter.println(";");
     }
-    writer.println();
+    javaWriter.println();
     List<String> javadoc = cfg.javadocForClass(containingTypeName);
     for (String doc : javadoc) {
-      writer.println(doc);
+      javaWriter.println(doc);
     }
-    writer.print("public class " + containingTypeName + " ");
+    javaWriter.print("public class " + containingTypeName + " ");
     boolean firstIteration = true;
     List<String> userSpecifiedInterfaces = cfg.implementedInterfaces(containingTypeName);
     for (String userInterface : userSpecifiedInterfaces) {
       if (firstIteration) {
-        writer.print("implements ");
+        javaWriter.print("implements ");
       }
       firstIteration = false;
-      writer.print(userInterface);
-      writer.print(" ");
-    }
-    writer.println("{");
-    writer.println();
-    writer.println("  StructAccessor accessor;");
-    writer.println();
-    writer.println("  private static final int mdIdx = MachineDescriptionRuntime.getStatic().ordinal();");
-    writer.println();
+      javaWriter.print(userInterface);
+      javaWriter.print(" ");
+    }
+    javaWriter.println("{");
+    javaWriter.println();
+    javaWriter.println("  StructAccessor accessor;");
+    javaWriter.println();
+    javaWriter.println("  private static final int mdIdx = MachineDescriptionRuntime.getStatic().ordinal();");
+    javaWriter.println();
     // generate all offset and size arrays
-    generateOffsetAndSizeArrays(writer, containingTypeName, structType, null); /* w/o offset */
+    generateOffsetAndSizeArrays(javaWriter, containingTypeName, structType, null); /* w/o offset */
     for (int i = 0; i < structType.getNumFields(); i++) {
       final Field field = structType.getField(i);
       final Type fieldType = field.getType();
@@ -917,14 +922,14 @@ public class JavaEmitter implements GlueEmitter {
                                        field + "\" in type \"" + name + "\")");
           }
 
-          generateOffsetAndSizeArrays(writer, fieldName, fieldType, field);
+          generateOffsetAndSizeArrays(javaWriter, fieldName, fieldType, field);
         } else if (fieldType.isArray()) {
             Type baseElementType = field.getType().asArray().getBaseElementType();
 
             if(!baseElementType.isPrimitive())
                 break;
 
-            generateOffsetAndSizeArrays(writer, fieldName, null, field); /* w/o size */
+            generateOffsetAndSizeArrays(javaWriter, fieldName, null, field); /* w/o size */
         } else {
           JavaType externalJavaType = null;
           try {
@@ -936,7 +941,7 @@ public class JavaEmitter implements GlueEmitter {
           }
           if (externalJavaType.isPrimitive()) {
             // Primitive type
-            generateOffsetAndSizeArrays(writer, fieldName, null, field); /* w/o size */
+            generateOffsetAndSizeArrays(javaWriter, fieldName, null, field); /* w/o size */
           } else {
             // FIXME
             LOG.log(WARNING, "Complicated fields (field \"{0}\" of type \"{1}\") not implemented yet", new Object[]{field, name});
@@ -946,27 +951,27 @@ public class JavaEmitter implements GlueEmitter {
         }
       }
     }
-    writer.println();
-
-    writer.println("  public static int size() {");
-    writer.println("    return "+containingTypeName+"_size[mdIdx];");
-    writer.println("  }");
-    writer.println();
-    writer.println("  public static " + containingTypeName + " create() {");
-    writer.println("    return create(Buffers.newDirectByteBuffer(size()));");
-    writer.println("  }");
-    writer.println();
-    writer.println("  public static " + containingTypeName + " create(java.nio.ByteBuffer buf) {");
-    writer.println("      return new " + containingTypeName + "(buf);");
-    writer.println("  }");
-    writer.println();
-    writer.println("  " + containingTypeName + "(java.nio.ByteBuffer buf) {");
-    writer.println("    accessor = new StructAccessor(buf);");
-    writer.println("  }");
-    writer.println();
-    writer.println("  public java.nio.ByteBuffer getBuffer() {");
-    writer.println("    return accessor.getBuffer();");
-    writer.println("  }");
+    javaWriter.println();
+
+    javaWriter.println("  public static int size() {");
+    javaWriter.println("    return "+containingTypeName+"_size[mdIdx];");
+    javaWriter.println("  }");
+    javaWriter.println();
+    javaWriter.println("  public static " + containingTypeName + " create() {");
+    javaWriter.println("    return create(Buffers.newDirectByteBuffer(size()));");
+    javaWriter.println("  }");
+    javaWriter.println();
+    javaWriter.println("  public static " + containingTypeName + " create(java.nio.ByteBuffer buf) {");
+    javaWriter.println("      return new " + containingTypeName + "(buf);");
+    javaWriter.println("  }");
+    javaWriter.println();
+    javaWriter.println("  " + containingTypeName + "(java.nio.ByteBuffer buf) {");
+    javaWriter.println("    accessor = new StructAccessor(buf);");
+    javaWriter.println("  }");
+    javaWriter.println();
+    javaWriter.println("  public java.nio.ByteBuffer getBuffer() {");
+    javaWriter.println("    return accessor.getBuffer();");
+    javaWriter.println("  }");
 
     for (int i = 0; i < structType.getNumFields(); i++) {
       final Field field = structType.getField(i);
@@ -983,12 +988,12 @@ public class JavaEmitter implements GlueEmitter {
               FunctionSymbol funcSym      = new FunctionSymbol(fieldName, funcType);
               MethodBinding  binding      = bindFunction(funcSym, containingType, containingCType, machDescJava);
               binding.findThisPointer(); // FIXME: need to provide option to disable this on per-function basis
-              writer.println();
+              javaWriter.println();
 
               // Emit public Java entry point for calling this function pointer
               JavaMethodBindingEmitter emitter =
                 new JavaMethodBindingEmitter(binding,
-                                             writer,
+                                             javaWriter,
                                              cfg.runtimeExceptionType(),
                                              cfg.unsupportedExceptionType(),
                                              true,
@@ -1008,7 +1013,7 @@ public class JavaEmitter implements GlueEmitter {
               // Emit private native Java entry point for calling this function pointer
               emitter =
                 new JavaMethodBindingEmitter(binding,
-                                             writer,
+                                             javaWriter,
                                              cfg.runtimeExceptionType(),
                                              cfg.unsupportedExceptionType(),
                                              false,
@@ -1029,7 +1034,7 @@ public class JavaEmitter implements GlueEmitter {
               // Emit (private) C entry point for calling this function pointer
               CMethodBindingEmitter cEmitter =
                 new CMethodBindingEmitter(binding,
-                                          newWriter,
+                                          jniWriter,
                                           structClassPkg,
                                           containingTypeName,
                                           true, // FIXME: this is optional at this point
@@ -1052,12 +1057,12 @@ public class JavaEmitter implements GlueEmitter {
                                        field + "\" in type \"" + name + "\")");
           }
 
-          writer.println();
-          generateGetterSignature(writer, false, fieldType.getName(), capitalizeString(fieldName));
-          writer.println(" {");
-          writer.println("    return " + fieldType.getName() + ".create( accessor.slice( " +
+          javaWriter.println();
+          generateGetterSignature(javaWriter, false, fieldType.getName(), capitalizeString(fieldName));
+          javaWriter.println(" {");
+          javaWriter.println("    return " + fieldType.getName() + ".create( accessor.slice( " +
                            fieldName+"_offset[mdIdx], "+fieldName+"_size[mdIdx] ) );");
-          writer.println(" }");
+          javaWriter.println(" }");
 
         } else if (fieldType.isArray()) {
 
@@ -1070,18 +1075,18 @@ public class JavaEmitter implements GlueEmitter {
             String capitalized = capitalizeString(fieldName);
 
             // Setter
-            writer.println();
-            generateSetterSignature(writer, false, containingTypeName, capitalized, paramType+"[]");
-            writer.println(" {");
-            writer.print  ("    accessor.set" + capitalizeString(paramType) + "sAt(" + fieldName+"_offset[mdIdx], val);");
-            writer.println("    return this;");
-            writer.println("  }");
-            writer.println();
+            javaWriter.println();
+            generateSetterSignature(javaWriter, false, containingTypeName, capitalized, paramType+"[]");
+            javaWriter.println(" {");
+            javaWriter.print  ("    accessor.set" + capitalizeString(paramType) + "sAt(" + fieldName+"_offset[mdIdx], val);");
+            javaWriter.println("    return this;");
+            javaWriter.println("  }");
+            javaWriter.println();
             // Getter
-            generateGetterSignature(writer, false, paramType+"[]", capitalized);
-            writer.println(" {");
-            writer.print  ("    return accessor.get" + capitalizeString(paramType) + "sAt(" + fieldName+"_offset[mdIdx], new " +paramType+"["+fieldType.asArray().getLength()+"]);");
-            writer.println(" }");
+            generateGetterSignature(javaWriter, false, paramType+"[]", capitalized);
+            javaWriter.println(" {");
+            javaWriter.print  ("    return accessor.get" + capitalizeString(paramType) + "sAt(" + fieldName+"_offset[mdIdx], new " +paramType+"["+fieldType.asArray().getLength()+"]);");
+            javaWriter.println(" }");
         } else {
           JavaType javaType = null;
 
@@ -1111,40 +1116,40 @@ public class JavaEmitter implements GlueEmitter {
                                    ", sizeDenominator "+sizeDenominator);
             }
 
-            writer.println();
+            javaWriter.println();
             // Setter
-            generateSetterSignature(writer, false, containingTypeName, capFieldName, javaTypeName);
-            writer.println(" {");
+            generateSetterSignature(javaWriter, false, containingTypeName, capFieldName, javaTypeName);
+            javaWriter.println(" {");
             if( fieldTypeNativeSizeFixed ) {
-                writer.println("    accessor.set" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx], val);");
+                javaWriter.println("    accessor.set" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx], val);");
             } else {
-                writer.println("    accessor.set" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx], val, MachineDescriptionRuntime.getStatic().md."+sizeDenominator+"SizeInBytes());");
+                javaWriter.println("    accessor.set" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx], val, MachineDescriptionRuntime.getStatic().md."+sizeDenominator+"SizeInBytes());");
             }
-            writer.println("    return this;");
-            writer.println("  }");
-            writer.println();
+            javaWriter.println("    return this;");
+            javaWriter.println("  }");
+            javaWriter.println();
 
             // Getter
-            generateGetterSignature(writer, false, javaTypeName, capFieldName);
-            writer.println(" {");
-            writer.print  ("    return ");
+            generateGetterSignature(javaWriter, false, javaTypeName, capFieldName);
+            javaWriter.println(" {");
+            javaWriter.print  ("    return ");
             if( fieldTypeNativeSizeFixed ) {
-                writer.println("accessor.get" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx]);");
+                javaWriter.println("accessor.get" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx]);");
             } else {
-                writer.println("accessor.get" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx], MachineDescriptionRuntime.getStatic().md."+sizeDenominator+"SizeInBytes());");
+                javaWriter.println("accessor.get" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx], MachineDescriptionRuntime.getStatic().md."+sizeDenominator+"SizeInBytes());");
             }
-            writer.println("  }");
+            javaWriter.println("  }");
           }
         }
       }
     }
-    emitCustomJavaCode(writer, containingTypeName);
-    writer.println("}");
-    writer.flush();
-    writer.close();
+    emitCustomJavaCode(javaWriter, containingTypeName);
+    javaWriter.println("}");
+    javaWriter.flush();
+    javaWriter.close();
     if (needsNativeCode) {
-      newWriter.flush();
-      newWriter.close();
+      jniWriter.flush();
+      jniWriter.close();
     }
   }
   @Override
@@ -1415,17 +1420,15 @@ public class JavaEmitter implements GlueEmitter {
   /**
    * @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
+   * @return a {@link PrintWriter} instance to write the class source file or <code>null</code> to suppress output!
    * @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();
-    if (parentDir != null)
-    {
-      File pDirFile = new File(parentDir);
-      pDirFile.mkdirs();
+    final File file = new File(filename);
+    final String parentDir = file.getParent();
+    if (parentDir != null) {
+        new File(parentDir).mkdirs();
     }
     return new PrintWriter(new BufferedWriter(new FileWriter(file)));
   }
diff --git a/src/java/com/jogamp/gluegen/structgen/CStruct.java b/src/java/com/jogamp/gluegen/structgen/CStruct.java
index 9d57196..be72cd8 100644
--- a/src/java/com/jogamp/gluegen/structgen/CStruct.java
+++ b/src/java/com/jogamp/gluegen/structgen/CStruct.java
@@ -33,21 +33,32 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 /**
- *
- * @author Michael Bien
- * @author Sven Gothel, et.al.
+ * @author Michael Bien, et al.
  */
-@Target(value = {ElementType.FIELD, ElementType.LOCAL_VARIABLE})
+@Target(value = {ElementType.TYPE, ElementType.PACKAGE, ElementType.FIELD, ElementType.LOCAL_VARIABLE})
 @Retention(value = RetentionPolicy.SOURCE)
 public @interface CStruct {
 
     /**
      * Relative path to the header file.
+     * <p>
+     * Mandatory.
+     * </p>
      */
     String header();
 
     /**
      * The name of the struct.
+     * <p>
+     * Mandatory for {@link ElementType.TYPE} and {@link ElementType.PACKAGE} annotations
+     * otherwise optional.
+     * </p>
      */
     String name() default "_default_";
+
+    /**
+     * The optional java name of the struct.
+     */
+    String jname() default "_default_";
+
 }
diff --git a/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java b/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java
index ee761a3..7b24967 100644
--- a/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java
+++ b/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java
@@ -30,12 +30,20 @@ package com.jogamp.gluegen.structgen;
 import com.jogamp.common.util.PropertyAccess;
 import com.jogamp.gluegen.GlueGen;
 import com.jogamp.gluegen.JavaEmitter;
+
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.io.Reader;
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
+
 import javax.annotation.processing.AbstractProcessor;
 import javax.annotation.processing.Filer;
 import javax.annotation.processing.Messager;
@@ -76,11 +84,11 @@ import jogamp.common.Debug;
  *
  * @author Michael Bien, et al.
  */
-@SupportedAnnotationTypes(value = {"com.jogamp.gluegen.structgen.CStruct"})
+@SupportedAnnotationTypes(value = {"com.jogamp.gluegen.structgen.CStruct", "com.jogamp.gluegen.structgen.CStructs"})
 @SupportedSourceVersion(SourceVersion.RELEASE_6)
 public class CStructAnnotationProcessor extends AbstractProcessor {
     private static final String DEFAULT = "_default_";
-    private static final boolean DEBUG;
+    static final boolean DEBUG;
 
     static {
         Debug.initSingleton();
@@ -131,71 +139,96 @@ public class CStructAnnotationProcessor extends AbstractProcessor {
     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
         final String user_dir = System.getProperty("user.dir");
 
-        final Set<? extends Element> elements = env.getElementsAnnotatedWith(CStruct.class);
-
-        for (Element element : elements) {
-
-            final String packageName = eltUtils.getPackageOf(element).toString();
+        final Set<? extends Element> cStructsElements = env.getElementsAnnotatedWith(CStructs.class);
+        for (Element structsElement : cStructsElements) {
+            final String packageName = eltUtils.getPackageOf(structsElement).toString();
+            final CStructs cstructs = structsElement.getAnnotation(CStructs.class);
+            if( null != cstructs ) {
+                final CStruct[] cstructArray = cstructs.value();
+                for(CStruct cstruct : cstructArray) {
+                    processCStruct(cstruct, structsElement, packageName, user_dir);
+                }
+            }
+        }
 
-            try {
-                final CStruct struct = element.getAnnotation(CStruct.class);
-                final String headerRelPath = struct.header();
-                final Element enclElement = element.getEnclosingElement();
+        final Set<? extends Element> cStructElements = env.getElementsAnnotatedWith(CStruct.class);
+        for (Element structElement : cStructElements) {
+            final String packageName = eltUtils.getPackageOf(structElement).toString();
+            final CStruct cstruct = structElement.getAnnotation(CStruct.class);
+            if( null != cstruct ) {
+                processCStruct(cstruct, structElement, packageName, user_dir);
+            }
+        }
+        return true;
+    }
 
-                System.err.println("CStruct: "+struct+", package "+packageName+", header "+headerRelPath);
-                if(DEBUG) {
-                    System.err.println("CStruct.0: user.dir: "+user_dir);
-                    System.err.println("CStruct.0: element: "+element+", .simpleName "+element.getSimpleName());
-                    System.err.println("CStruct.0: enclElement: "+enclElement+", .simpleName "+enclElement.getSimpleName()+", .package "+eltUtils.getPackageOf(enclElement).toString());
+    private void processCStruct(final CStruct struct, final Element element, final String packageName, final String user_dir) {
+        try {
+            final String headerRelPath = struct.header();
+            final Element enclElement = element.getEnclosingElement();
+            final boolean isPackageOrType = null == enclElement;
+
+            System.err.println("CStruct: "+struct+", package "+packageName+", header "+headerRelPath);
+            if(DEBUG) {
+                System.err.println("CStruct.0: user.dir: "+user_dir);
+                System.err.println("CStruct.0: element: "+element+", .simpleName "+element.getSimpleName());
+                System.err.print("CStruct.0: isPackageOrType "+isPackageOrType+", enclElement: "+enclElement);
+                if( !isPackageOrType ) {
+                    System.err.println(", .simpleName "+enclElement.getSimpleName()+", .package "+eltUtils.getPackageOf(enclElement).toString());
+                } else {
+                    System.err.println("");
                 }
+            }
+            if( isPackageOrType && struct.name().equals(DEFAULT) ) {
+                throw new IllegalArgumentException("CStruct annotation on package or type must have name specified: "+struct+" @ "+element);
+            }
 
-                final File headerFile;
-                {
-                    File f = locateSource(packageName, headerRelPath);
+            final File headerFile;
+            {
+                File f = locateSource(packageName, headerRelPath);
+                if( null == f ) {
+                    f = locateSource("", headerRelPath);
                     if( null == f ) {
-                        f = locateSource("", headerRelPath);
-                        if( null == f ) {
-                            // bail out
-                            throw new RuntimeException("Could not locate header "+headerRelPath+", package "+packageName);
-                        }
+                        // bail out
+                        throw new RuntimeException("Could not locate header "+headerRelPath+", package "+packageName);
                     }
-                    headerFile = f;
                 }
+                headerFile = f;
+            }
 
-                final String root;
-                {
-                    String root0 = headerFile.getAbsolutePath();
-                    root0 = root0.substring(0, root0.length()-headerFile.getName().length()-1);
-                    root = root0.substring(0, root0.length()-packageName.length()) +"..";
-                }
-                System.err.println("CStruct: "+headerFile+", abs: "+headerFile.isAbsolute()+", root "+root);
-
-                generateStructBinding(element, struct, root, packageName, headerFile);
-            } catch (IOException ex) {
-                throw new RuntimeException("IOException while processing!", ex);
+            final String rootOut, headerParent;
+            {
+                final String root0 = headerFile.getAbsolutePath();
+                headerParent = root0.substring(0, root0.length()-headerFile.getName().length()-1);
+                rootOut = headerParent.substring(0, headerParent.length()-packageName.length()) + "..";
             }
+            System.err.println("CStruct: "+headerFile+", abs: "+headerFile.isAbsolute()+", headerParent "+headerParent+", rootOut "+rootOut);
+
+            generateStructBinding(element, struct, isPackageOrType, rootOut, packageName, headerFile, headerParent);
+        } 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 {
+    private void generateStructBinding(Element element, CStruct struct, boolean isPackageOrType, String rootOut, String pakage, File header, String headerParent) 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);
+        final boolean useStructName = !struct.name().equals(DEFAULT);
+        final String structName = useStructName ? struct.name() : declaredType;
+        final boolean useJavaName = !struct.jname().equals(DEFAULT);
+
+        final String finalType = useJavaName ? struct.jname() : ( !isPackageOrType ? declaredType : structName );
+        System.err.println("CStruct: Generating struct accessor for struct: "+structName+" -> "+finalType+" [struct.name "+struct.name()+", struct.jname "+struct.jname()+", declaredType "+declaredType+"]");
+        if( generatedStructs.contains(finalType) ) {
+            messager.printMessage(Kind.NOTE, "struct "+structName+" already defined elsewhere, skipping.", 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 + File.separator + outputPath;
+        final String outputPath1 = outputDirAbs ? outputPath : rootOut + File.separator + outputPath;
         final String config = outputPath1 + File.separator + header.getName() + ".cfg";
         final File configFile = new File(config);
         if(DEBUG) {
@@ -209,7 +242,8 @@ public class CStructAnnotationProcessor extends AbstractProcessor {
             writer = new FileWriter(configFile);
             writer.write("Package "+pakage+"\n");
             writer.write("EmitStruct "+structName+"\n");
-            if(!struct.name().equals(DEFAULT)) {
+            if( finalType != structName ) {
+                // We allow renaming the structType to the element's declaredType (FIELD annotation only)
                 writer.write("RenameJavaType " + struct.name()+" " + declaredType +"\n");
             }
         } finally {
@@ -217,16 +251,23 @@ public class CStructAnnotationProcessor extends AbstractProcessor {
                 writer.close();
             }
         }
-
-        // TODO: Handle exceptions .. suppressed by Gluegen.main(..) ?
-        GlueGen.main(
-                //                "-I"+path+"/build/",
-                "-O" + outputPath1,
-                "-E" + AnnotationProcessorJavaStructEmitter.class.getName(),
-                "-C" + config,
-                header.getPath());
+        final List<String> cfgFiles = new ArrayList<String>();
+        cfgFiles.add(config);
+        final List<String> includePaths = new ArrayList<String>();
+        includePaths.add(headerParent);
+        includePaths.add(outputPath1);
+        final Reader reader;
+        final String filename = header.getPath();
+        try {
+            reader = new BufferedReader(new FileReader(filename));
+        } catch (FileNotFoundException ex) {
+            throw new RuntimeException("input file not found", ex);
+        }
+        new GlueGen().run(reader, filename, AnnotationProcessorJavaStructEmitter.class,
+                          includePaths, cfgFiles, outputPath1, false /* copyCPPOutput2Stderr */);
 
         configFile.delete();
+        generatedStructs.add(finalType);
     }
 
     public static class AnnotationProcessorJavaStructEmitter extends JavaEmitter {
@@ -234,14 +275,17 @@ public class CStructAnnotationProcessor extends AbstractProcessor {
         @Override
         protected PrintWriter openFile(String filename, String simpleClassName) throws IOException {
 
+            if( generatedStructs.contains(simpleClassName) ) {
+                System.err.println("skipping -> " + simpleClassName);
+                return null;
+            }
+
             // look for recursive generated structs... keep it DRY
             if( !simpleClassName.endsWith("32") &&
                 !simpleClassName.endsWith("64") ) {
-
-                System.out.println("generating -> " + simpleClassName);
+                System.err.println("generating -> " + simpleClassName);
                 generatedStructs.add(simpleClassName);
             }
-
             return super.openFile(filename, simpleClassName);
         }
 
diff --git a/src/java/com/jogamp/gluegen/structgen/CStructs.java b/src/java/com/jogamp/gluegen/structgen/CStructs.java
new file mode 100644
index 0000000..1023d3e
--- /dev/null
+++ b/src/java/com/jogamp/gluegen/structgen/CStructs.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+/**
+ * Multiple {@link CStruct} elements
+ */
+@Target(value = {ElementType.TYPE, ElementType.PACKAGE, ElementType.FIELD, ElementType.LOCAL_VARIABLE})
+@Retention(value = RetentionPolicy.SOURCE)
+public @interface CStructs {
+
+    /**
+     * Multiple {@link CStruct} elements.
+     */
+    CStruct[] value();
+}
diff --git a/src/junit/com/jogamp/gluegen/test/junit/structgen/BuildStruct01.java b/src/junit/com/jogamp/gluegen/test/junit/structgen/BuildStruct01.java
new file mode 100644
index 0000000..7824e55
--- /dev/null
+++ b/src/junit/com/jogamp/gluegen/test/junit/structgen/BuildStruct01.java
@@ -0,0 +1,34 @@
+package com.jogamp.gluegen.test.junit.structgen;
+
+import com.jogamp.gluegen.structgen.CStruct;
+
+/**
+ * Class simply triggering CStruct annotation processor to generate the types 'RenderingConfig' etc.
+ * <p>
+ * Due to Java8 issues, see Bug 923,
+ * using {@link package-info} is more elegant to kick-off the annotation processor.
+ * </p>
+ */
+public class BuildStruct01 {
+
+    // 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")
+    // MyRenderingConfig config;
+
+    /**
+     * Java8: We cannot use type 'RenderingConfig' yet (bug?) even if not compiling.
+     * Hence we force the type-name via 'jname' and use a dummy variable!
+     */
+    @CStruct(name="RenderingConfig", jname="RenderingConfig", header="TestStruct01.h")
+    boolean dummy1;
+
+    @CStruct(name="Pixel", jname="Pixel", header="TestStruct02.h")
+    boolean dummy2;
+
+    public static void initSingleton() {}
+}
diff --git a/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStruct02.h b/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStruct02.h
new file mode 100644
index 0000000..64fda78
--- /dev/null
+++ b/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStruct02.h
@@ -0,0 +1,14 @@
+//
+// TestStruct02.h
+//
+
+#include "TestStruct01.h"
+
+typedef struct {
+    float r, g, b, a;
+} Col4f;
+
+typedef struct {
+	Col4f color;
+	Vec3f pos;
+} Pixel;
diff --git a/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStructGen01.java b/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStructGen01.java
index 23b1624..74f0552 100644
--- a/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStructGen01.java
+++ b/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStructGen01.java
@@ -1,26 +1,23 @@
 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.BeforeClass;
 import org.junit.Test;
-
 import org.junit.FixMethodOrder;
 import org.junit.runners.MethodSorters;
 
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 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;
-    
+    @BeforeClass
+    public static void init() {
+        // Enforce dependency,
+        // i.e. CStruct annotation processor to generate the types 'RenderingConfig' etc.
+        BuildStruct01.initSingleton();
+    }
+
     @Test
     public void test01() {
         RenderingConfig config = RenderingConfig.create();
@@ -46,10 +43,10 @@ public class TestStructGen01 extends JunitTracer {
         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);
     }
-    
+
 }
diff --git a/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStructGen02.java b/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStructGen02.java
new file mode 100644
index 0000000..a8f0e52
--- /dev/null
+++ b/src/junit/com/jogamp/gluegen/test/junit/structgen/TestStructGen02.java
@@ -0,0 +1,51 @@
+package com.jogamp.gluegen.test.junit.structgen;
+
+import com.jogamp.junit.util.JunitTracer;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.FixMethodOrder;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class TestStructGen02 extends JunitTracer {
+
+    @BeforeClass
+    public static void init() {
+        // Enforce dependency,
+        // i.e. CStruct annotation processor to generate the types 'RenderingConfig' etc.
+        BuildStruct01.initSingleton();
+    }
+
+    @Test
+    public void test01() {
+        final Pixel pixel = Pixel.create();
+        final Col4f color = pixel.getColor();
+        color.setR(1f);
+        color.setG(2f);
+        color.setB(3f);
+        color.setA(4f);
+        final Vec3f pos = pixel.getPos();
+        pos.setX(0.5f);
+        pos.setY(0.6f);
+        pos.setZ(0.7f);
+
+        Pixel pixel2 = Pixel.create(pixel.getBuffer());
+        final Col4f color2 = pixel2.getColor();
+        Assert.assertEquals(color.getR(), color2.getR(), 0.0001f);
+        Assert.assertEquals(color.getG(), color2.getG(), 0.0001f);
+        Assert.assertEquals(color.getB(), color2.getB(), 0.0001f);
+        Assert.assertEquals(color.getA(), color2.getA(), 0.0001f);
+        final Vec3f pos2 = pixel2.getPos();
+        Assert.assertEquals(pos.getX(), pos2.getX(), 0.0001f);
+        Assert.assertEquals(pos.getY(), pos2.getY(), 0.0001f);
+        Assert.assertEquals(pos.getZ(), pos2.getZ(), 0.0001f);
+    }
+
+    public static void main(String args[]) {
+        String tstname = TestStructGen02.class.getName();
+        org.junit.runner.JUnitCore.main(tstname);
+    }
+
+}
diff --git a/src/junit/com/jogamp/gluegen/test/junit/structgen/package-info.java b/src/junit/com/jogamp/gluegen/test/junit/structgen/package-info.java
new file mode 100644
index 0000000..d009c5f
--- /dev/null
+++ b/src/junit/com/jogamp/gluegen/test/junit/structgen/package-info.java
@@ -0,0 +1,15 @@
+/**
+ * Package scope generation of {@link CStruct}s
+ * avoiding Java8 issues w/ annotation processing
+ * where the generated class is not yet available.
+ * <p>
+ * See Bug 923.
+ * </p>
+ * @see BuildStruct01
+ */
+@CStructs({@CStruct(name="RenderingConfig", header="TestStruct01.h"), @CStruct(name="Pixel", header="TestStruct02.h")})
+package com.jogamp.gluegen.test.junit.structgen;
+
+import com.jogamp.gluegen.structgen.CStructs;
+import com.jogamp.gluegen.structgen.CStruct;
+
-- 
cgit v1.2.3