/*
* Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
* Copyright (c) 2010 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:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution 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 Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
* ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
* DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for use
* in the design, construction, operation or maintenance of any nuclear
* facility.
*
* Sun gratefully acknowledges that this software was originally authored
* and developed by Kenneth Bradley Russell and Christopher John Kline.
*/
package com.jogamp.gluegen;
import com.jogamp.common.GlueGenVersion;
import java.io.*;
import java.util.*;
import antlr.*;
import com.jogamp.gluegen.cgram.*;
import com.jogamp.gluegen.cgram.types.*;
import com.jogamp.gluegen.pcpp.*;
import static java.lang.System.*;
/**
* Glue code generator for C functions and data structures.
*/
public class GlueGen implements GlueEmitterControls {
static{
Logging.init();
}
private List forcedStructNames = new ArrayList();
private PCPP preprocessor;
// State for SymbolFilters
private List constants;
private List functions;
private static boolean debug = false;
public static boolean debug() { return debug; }
public void forceStructEmission(String typedefName) {
forcedStructNames.add(typedefName);
}
public String findHeaderFile(String headerFileName) {
return preprocessor.findFile(headerFileName);
}
public void runSymbolFilter(SymbolFilter filter) {
filter.filterSymbols(constants, functions);
List newConstants = filter.getConstants();
List newFunctions = filter.getFunctions();
if (newConstants != null) {
constants = newConstants;
}
if (newFunctions != null) {
functions = newFunctions;
}
}
@SuppressWarnings("unchecked")
public void run(final Reader reader, final String filename, Class> emitterClass, List includePaths, List cfgFiles, String outputRootDir, boolean copyPCPPOutput2Stderr) {
try {
File out = File.createTempFile("PCPPTemp", ".pcpp");
FileOutputStream outStream = new FileOutputStream(out);
if(debug) {
System.err.println("PCPP output at (persistent): " + out.getAbsolutePath());
} else {
out.deleteOnExit();
}
preprocessor = new PCPP(includePaths, debug, copyPCPPOutput2Stderr);
preprocessor.addDefine("__GLUEGEN__", "2");
preprocessor.setOut(outStream);
preprocessor.run(reader, filename);
outStream.flush();
outStream.close();
FileInputStream inStream = new FileInputStream(out);
DataInputStream dis = new DataInputStream(inStream);
GnuCLexer lexer = new GnuCLexer(dis);
lexer.setTokenObjectClass(CToken.class.getName());
lexer.initialize();
// Parse the input expression.
GnuCParser parser = new GnuCParser(lexer);
// set AST node type to TNode or get nasty cast class errors
parser.setASTNodeClass(TNode.class.getName());
TNode.setTokenVocabulary(GNUCTokenTypes.class.getName());
// invoke parser
try {
parser.translationUnit();
} catch (RecognitionException e) {
throw new RuntimeException("Fatal IO error", e);
} catch (TokenStreamException e) {
throw new RuntimeException("Fatal IO error", e);
}
HeaderParser headerParser = new HeaderParser();
headerParser.setDebug(debug);
TypeDictionary td = new TypeDictionary();
headerParser.setTypedefDictionary(td);
TypeDictionary sd = new TypeDictionary();
headerParser.setStructDictionary(sd);
// set AST node type to TNode or get nasty cast class errors
headerParser.setASTNodeClass(TNode.class.getName());
// walk that tree
headerParser.translationUnit(parser.getAST());
dis.close();
inStream.close();
/**
// For debugging: Dump type dictionary and struct dictionary to System.err
if(debug) {
td.dumpDictionary(err, "All Types");
sd.dumpDictionary(err, "All Structs");
} */
// At this point we have all of the pieces we need in order to
// generate glue code: the #defines to constants, the set of
// typedefs, and the set of functions.
GlueEmitter emit = null;
if (emitterClass == null) {
emit = new JavaEmitter();
} else {
try {
emit = (GlueEmitter) emitterClass.newInstance();
} catch (Exception e) {
throw new RuntimeException("Exception occurred while instantiating emitter class.", e);
}
}
for (String config : cfgFiles) {
emit.readConfigurationFile(config);
}
if (null != outputRootDir && outputRootDir.trim().length() > 0) {
if (emit instanceof JavaEmitter) {
// FIXME: hack to interfere with the *Configuration setting via commandlines
JavaEmitter jemit = (JavaEmitter) emit;
if (null != jemit.getConfig()) {
jemit.getConfig().setOutputRootDir(outputRootDir);
}
}
}
// Repackage the enum and #define statements from the parser into a common format
// so that SymbolFilters can operate upon both identically
constants = new ArrayList();
for (Object elem : headerParser.getEnums()) {
EnumType enumeration = (EnumType) elem;
String enumName = enumeration.getName();
if (enumName.equals("")) {
enumName = null;
}
// iterate over all values in the enumeration
for (int i = 0; i < enumeration.getNumEnumerates(); ++i) {
String enumElementName = enumeration.getEnumName(i);
String value = String.valueOf(enumeration.getEnumValue(i));
constants.add(new ConstantDefinition(enumElementName, value, true, enumName));
}
}
for (Object elem : lexer.getDefines()) {
Define def = (Define) elem;
constants.add(new ConstantDefinition(def.getName(), def.getValue(), false, null));
}
functions = headerParser.getParsedFunctions();
// begin emission of glue code
emit.beginEmission(this);
emit.beginDefines();
Set emittedDefines = new HashSet(100);
// emit java equivalent of enum { ... } statements
for (ConstantDefinition def : constants) {
if (!emittedDefines.contains(def.getName())) {
emittedDefines.add(def.getName());
String comment = null;
Set aliases = def.getAliases();
if (aliases != null) {
comment = "Alias for: ";
for (String alias : aliases) {
comment += " " + alias;
}
comment += "
";
}
if (def.getEnumName() != null) {
String enumName = "Defined as part of enum type \"" + def.getEnumName() + "\"";
if (comment == null) {
comment = enumName;
} else {
comment += "
\n" + enumName;
}
}
emit.emitDefine(def, comment);
}
}
emit.endDefines();
// Iterate through the functions finding structs that are referenced in
// the function signatures; these will be remembered for later emission
ReferencedStructs referencedStructs = new ReferencedStructs();
for (FunctionSymbol sym : functions) {
// FIXME: this doesn't take into account the possibility that some of
// the functions we send to emitMethodBindings() might not actually be
// emitted (e.g., if an Ignore directive in the JavaEmitter causes it
// to be skipped).
sym.getType().visit(referencedStructs);
}
// Normally only referenced types will be emitted. The user can force a
// type to be emitted via a .cfg file directive. Those directives are
// processed here.
for (String name : forcedStructNames) {
Type type = td.get(name);
if (type == null) {
err.println("WARNING: during forced struct emission: struct \"" + name + "\" not found");
} else if (!type.isCompound()) {
err.println("WARNING: during forced struct emission: type \"" + name + "\" was not a struct");
} else {
type.visit(referencedStructs);
}
}
// Lay out structs
emit.beginStructLayout();
for (Iterator iter = referencedStructs.results(); iter.hasNext();) {
Type t = iter.next();
if (t.isCompound()) {
emit.layoutStruct(t.asCompound());
} else if (t.isPointer()) {
PointerType p = t.asPointer();
CompoundType c = p.getTargetType().asCompound();
emit.layoutStruct(c);
}
}
emit.endStructLayout();
// Emit structs
emit.beginStructs(td, sd, headerParser.getCanonMap());
for (Iterator iter = referencedStructs.results(); iter.hasNext();) {
Type t = iter.next();
if (t.isCompound()) {
emit.emitStruct(t.asCompound(), null);
} else if (t.isPointer()) {
PointerType p = t.asPointer();
CompoundType c = p.getTargetType().asCompound();
assert p.hasTypedefedName() && c.getName() == null : "ReferencedStructs incorrectly recorded pointer type " + p;
emit.emitStruct(c, p.getName());
}
}
emit.endStructs();
// emit java and C code to interface with the native functions
emit.beginFunctions(td, sd, headerParser.getCanonMap());
emit.emitFunctions(functions);
emit.endFunctions();
// end emission of glue code
emit.endEmission();
} catch (Exception e) {
throw new RuntimeException("Exception occurred while generating glue code.", e);
}
}
public static void main(String... args) {
if (args.length == 0) {
System.err.println(GlueGenVersion.getInstance());
usage();
}
Reader reader = null;
String filename = null;
String emitterFQN = null;
String outputRootDir = null;
List cfgFiles = new ArrayList();
boolean copyCPPOutput2Stderr = false;
List includePaths = new ArrayList();
for (int i = 0; i < args.length; i++) {
if (i < args.length - 1) {
String arg = args[i];
if (arg.startsWith("-I")) {
String[] paths = arg.substring(2).split(getProperty("path.separator"));
includePaths.addAll(Arrays.asList(paths));
} else if (arg.startsWith("-O")) {
outputRootDir = arg.substring(2);
} else if (arg.startsWith("-E")) {
emitterFQN = arg.substring(2);
} else if (arg.startsWith("-C")) {
cfgFiles.add(arg.substring(2));
} else if (arg.equals("--debug")) {
debug=true;
} else if (arg.equals("--dumpCPP")) {
copyCPPOutput2Stderr=true;
} else {
usage();
}
} else {
String arg = args[i];
if (arg.equals("-")) {
reader = new InputStreamReader(in);
filename = "standard input";
} else {
if (arg.startsWith("-")) {
usage();
}
filename = arg;
try {
reader = new BufferedReader(new FileReader(filename));
} catch (FileNotFoundException ex) {
throw new RuntimeException("input file not found", ex);
}
}
}
}
try {
Class> emitterClass = emitterFQN == null ? null : Class.forName(emitterFQN);
new GlueGen().run(reader, filename, emitterClass, includePaths, cfgFiles, outputRootDir, copyCPPOutput2Stderr);
} catch (ClassNotFoundException ex) {
throw new RuntimeException("specified emitter class was not in the classpath", ex);
}
}
//----------------------------------------------------------------------
// Internals only below this point
//
private static void usage() {
out.println("Usage: java GlueGen [-I...] [-Eemitter_class_name] [-Ccfg_file_name...] ");
out.println();
out.println("Runs C header parser on input file or standard input, first");
out.println("passing input through minimal pseudo-C-preprocessor. Use -I");
out.println("command-line arguments to specify the search path for #includes.");
out.println("Emitter class name can be specified with -E option: i.e.,");
out.println("-Ecom.jogamp.gluegen.JavaEmitter (the default). Use");
out.println("-Ecom.jogamp.gluegen.DebugEmitter to print recognized entities");
out.println("(#define directives to constant numbers, typedefs, and function");
out.println("declarations) to standard output. Emitter-specific configuration");
out.println("file or files can be specified with -C option; e.g,");
out.println("-Cjava-emitter.cfg.");
out.println(" --debug enables debug mode");
out.println(" --dumpCPP directs PCPP to dump all output to stderr as well");
exit(1);
}
}