1 package com.ochafik.lang.jnaerator;
2
3 import java.io.File;
4 import java.io.FileNotFoundException;
5 import java.io.IOException;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Collections;
9 import java.util.Comparator;
10 import java.util.List;
11 import java.util.NoSuchElementException;
12 import java.util.regex.Matcher;
13 import java.util.regex.Pattern;
14
15 public class JNAeratorCommandLineArgs {
16 static abstract class ArgsParser {
17 public static class ParsedArg {
18 public OptionDef def;
19 public Object[] params;
20
21 public File getFileParam(String name) {
22 return (File)params[def.getParam(name).position];
23 }
24 public String getStringParam(String name) {
25 return (String)params[def.getParam(name).position];
26 }
27 public int getIntParam(String name) {
28 return (Integer)params[def.getParam(name).position];
29 }
30 public File getFileParam(int pos) {
31 return (File)params[pos];
32 }
33 public String getStringParam(int pos) {
34 return (String)params[pos];
35 }
36 public int getIntParam(int pos) {
37 return (Integer)params[pos];
38 }
39 }
40 Object convertArg(String arg, OptionDef.Type type) throws FileNotFoundException {
41 switch (type) {
42 case OptionalFile:
43 boolean opt = arg.endsWith("?");
44 File f = new File(opt ? arg.substring(0, arg.length() - 1 ) : arg);
45 if (!f.exists()) {
46 if (opt)
47 return null;
48 throw new FileNotFoundException(f.toString());
49 }
50 return f;
51 case File:
52 return new File(arg);
53 case Int:
54 return Integer.parseInt(arg);
55 case String:
56 return arg;
57 case ExistingDir:
58 f = new File(arg);
59 if (!f.isDirectory())
60 throw new FileNotFoundException(f.toString());
61 break;
62 case ExistingFile:
63 f = new File(arg);
64 if (!f.isFile())
65 throw new FileNotFoundException(f.toString());
66 return f;
67 case ExistingFileOrDir:
68 f = new File(arg);
69 if (!f.exists())
70 throw new FileNotFoundException(f.toString());
71 return f;
72 case OutputDir:
73 f = new File(arg);
74 if (f.isFile())
75 throw new FileNotFoundException("Expected directory, found file : " + f.toString());
76 f.getAbsoluteFile().getParentFile().mkdirs();
77 return f;
78 case OutputFile:
79 f = new File(arg);
80 if (f.isDirectory())
81 throw new FileNotFoundException("Expected file, found directory : " + f.toString());
82 f.getAbsoluteFile().getParentFile().mkdirs();
83 return f;
84 }
85 throw new UnsupportedOperationException();
86 }
87 public void parse(List<String> args) throws Exception {
88 for (int i = 0; i < args.size(); i++) {
89 String arg = args.get(i);
90 OptionDef defaultOpt = null;
91 for (OptionDef opt : OptionDef.values()) {
92 if (opt.switchPattern == null) {
93 defaultOpt = opt;
94 continue;
95 }
96 Matcher m = opt.switchPattern.matcher(arg);
97 if (m.matches()) {
98 ParsedArg pa = new ParsedArg();
99 pa.def = opt;
100 pa.params = new Object[opt.args.length];
101 int iArg = 0;
102 for (int iGroup = 0; iGroup < m.groupCount(); iGroup++) {
103 String gp = m.group(iGroup + 1);
104 if (gp == null)
105 continue;
106 pa.params[iArg] = convertArg(gp, opt.args[iArg].type);
107 iArg++;
108 }
109 for (; iArg < opt.args.length; iArg++)
110 pa.params[iArg] = convertArg(args.get(++i), opt.args[iArg].type);
111
112 List<String> parsed = parsed(pa);
113 if (parsed == null)
114 return;
115 args.addAll(i + 1, parsed);
116 defaultOpt = null;
117 break;
118 }
119 }
120 if (defaultOpt != null) {
121 ParsedArg pa = new ParsedArg();
122 pa.def = defaultOpt;
123 pa.params = new Object[] { convertArg(arg, defaultOpt.args[0].type) };
124 args.addAll(i + 1, parsed(pa));
125 }
126 }
127 finished();
128 }
129
130
131 abstract List<String> parsed(ParsedArg a) throws Exception;
132 abstract void finished() throws IOException;
133 }
134
135 public enum OptionDef {
136
137 IncludeArgs( "@(.+)?", "Read command-line arguments from a file. File may contain multiple lines (those beginning with \"//\" will be skipped), file wildcards will be resolved within the file content, as well as variables substitutions : $(someEnvOrJavaVarName), with $(DIR) being the parent directory of the current arguments file.", new ArgDef(Type.ExistingFile, "argumentsFile.jnaerator")),
138 OutputDir( "-o", "Output directory for all artifacts", new ArgDef(Type.OutputDir, "outDir")),
139 ExtractSymbols( "-scanSymbols", "Extract, unmangle and parse the symbols all listed shared libraries"),
140 AddIncludePath( "-I(.+)?", "Add a directory to the include path. See doc of JNAERATOR_INCLUDE_PATH", new ArgDef(Type.File, "dir")),
141 AddFrameworksPath( "-F(.+)?", "Add a directory to the frameworks path. See doc of JNAERATOR_FRAMEWORKS_PATH", new ArgDef(Type.File, "dir")),
142 FrameworksPath( "-frameworksPath", "See doc of JNAERATOR_FRAMEWORKS_PATH", new ArgDef(Type.String, "path1:path2...")),
143 Framework( "-framework", "JNAerate a framework using its headers and its *.bridgesupport files if available", new ArgDef(Type.String, "frameworkName")),
144 LimitComments( "-limitComments", "Avoid useless comments (source file + line, skipped items...)"),
145 NoComments( "-noComments", "Don't output any member comment."),
146 NoMangling( "-noMangling", "Don't output any C++ name mangling information (may cause C++-decorated symbols not to be found at execution time)."),
147 NoCPP( "-nocpp", "Do not define the __cplusplus symbol"),
148 GUI( "-gui", "Show minimalist progression GUI"),
149 NoRuntime( "-noRuntime", "Don't copy runtime classes to JAR output"),
150 JarOut( "-jar", "Jar file where all generated sources and the compiled classes go", new ArgDef(Type.OutputFile, "outFile")),
151 WCharAsShort( "-wcharAsShort", "Force treatment of wchar_t as short (char by default)"),
152
153 Studio( "-studio", "Launch JNAeratorStudio"),
154 ScalaOut( "-scalaOut", "[Experimental] Output Scala wrappers (callbacks implicits...)", new ArgDef(Type.OutputDir, "outDir")),
155 NoStringReturns( "-noStringReturns", "Prevent const char* and const wchar_t* return types from being converted to String and WString."),
156 Project( "-project", "Read Visual Studio 2008 project or solution file and use the configuration specified (e.g. \"Release|Win32\").", new ArgDef(Type.ExistingFile, "solutionFile"), new ArgDef(Type.String, "\"Config|Platform\"")),
157 NoAuto( "-noAuto", "No auto-configuration of preprocessor symbols and paths"),
158 GCCLong( "-gccLong", "Use GCC convention for size of 'long' (4 bytes on 32 bits platforms, 8 bytes on 64 bits platforms)."),
159 SizeAsLong( "-sizeAsLong", "Treat size_t and ptrdiff_t values as 'long' values. ONLY HERE FOR COMPATIBILITY WITH PREVIOUS VERSIONS, WILL EVENTUALLY BE REMOVED."),
160 Direct( "-direct", "JNAerate libraries that use JNA's faster direct call convention"),
161 PreferJavac( "-preferJavac", "Use Sun's Javac compiler instead of Eclipse's ecj, if possible"),
162 StructsInLibrary( "-structsInLibrary", "Force structs to be JNAerated as inner classes of their declaring libraries (otherwise, each top-level structure is defined as a top-level class in its library's package)"),
163 CurrentPackage( "-package", "Set the Java package in which all the output will reside (by default, set to the library name).", new ArgDef(Type.String, "forcedPackageName")),
164 RecursedExtensions( "-allowedFileExts", "Colon-separated list of file extensions used to restrict files used when recursing on directories, or \"*\" to parse all files (by default = " + JNAeratorConfig.DEFAULT_HEADER_EXTENSIONS + ")", new ArgDef(Type.String, "extensions")),
165 IfRegexMatch( "-ifRegexMatch", "Conditional evaluation of an argument if a java system property matches a regular expression", new ArgDef(Type.String, "javaProperty"), new ArgDef(Type.String, "regex"), new ArgDef(Type.String, "thenArg"), new ArgDef(Type.String, "elseArg")),
166 DefineMacro( "-D([^=]*)(?:=(.*))?", "Define a macro symbol", new ArgDef(Type.String, "name"), new ArgDef(Type.String, "value")),
167 RootPackage( "-root(?:Package)?", "Define the root package for all output classes", new ArgDef(Type.String, "package")),
168 CurrentLibrary( "-library", "Define the name of the output library. This is a state parameter, it will affect all files listed after it, until another -library switch is provided. It does not affect sources included from a project file (Visual Studio...).\n" +
169 "C functions exported in library \"test\" will end up in class \"TestLibrary\", for instance. \n" +
170 "The name of the library is the one fed to JNA to find the shared library, so library \"test\" must be in \"test.dll\" on Windows, \"libtest.dylib\" on Mac OS X and \"libtest.so\" on other Unices.\n" +
171 "Note that a special hack is done for library \"c\" on Windows systems : the output name is set to \"msvcrt\" instead of \"c\".\n",
172 new ArgDef(Type.String, "libName")),
173 DefaultLibrary( "-defaultLibrary", "Name of output library for elements declared in files not covered by a ${CurrentLibrary} switch", new ArgDef(Type.String, "libName")),
174 Help( "-h(?:elp)?", "Show command line arguments help"),
175 EntryName( "-entryClass", "Generate a class _entryclassName.EntryClassName_ that will contain all of the jnaerated libraries instances. User code will just need to static import or derive from this class to access to the instances.", new ArgDef(Type.String, "entryClassName")),
176
177 Verbose( "-v(?:erbose)?", "Verbose output (both console and files)"),
178 ChoicesOut( "-choicesOut", "Write the function alternative choices made (automatically set when ${Verbose} is used).", new ArgDef(Type.OutputFile, "outFile")),
179 ChoicesIn( "-choices", "Read the function alternative choices from a file in the format used by -choicesOut.", new ArgDef(Type.ExistingFile, "choicesFile")),
180 PreprocessingOut( "-preprocessingOut", "Write the preprocessor output in a file (automatically set when ${Verbose} is used).", new ArgDef(Type.OutputFile, "outFile")),
181 ExtractionOut( "-extractionOut", "Write the symbols extracted from libraries in a file (automatically set when ${Verbose} is used).", new ArgDef(Type.OutputFile, "outFile")),
182 BridgeSupportOutFile("-bridgeSupportOut", "Write the definitions extracted from bridgesupport files in a file (automatically set when ${Verbose} is used).", new ArgDef(Type.OutputFile, "outFile")),
183 WikiDoc( "-wikiHelp", "Output a wiki-friendly help"),
184 Arch( "-arch", "Define the current architecture for libraries (state variable)", new ArgDef(Type.String, "archName")),
185 MacrosOut( "-macrosOut", "Write the preprocessor macros in a file (automatically set when ${Verbose} is used).", new ArgDef(Type.OutputFile, "outFile")),
186 NoPrimitiveArrays( "-noPrimitiveArrays", "Never output primitive arrays for function arguments (use NIO buffers instead)"),
187 File( null, "Any header (or directory containing headers at any level of hierarchy), shared library, *.bridgesupport file or *.jnaerator file", new ArgDef(Type.OptionalFile, "file")),
188 NoPreprocessing( "-fpreprocessed", "Consider source files as being already preprocessed (preprocessor won't be run)"),
189 NoCompile( "(?i)-noComp", "Do not compile JNAerated headers"),
190 NoJAR( "(?i)-noJar", "Do not create an output JAR"),
191
192 NoLibBundle( "(?i)-noLibBundle", "Do not bundle libraries in output JAR"),
193 MaxConstructedFields(
194 "-maxConstrFields", "Maximum number of fields allowed for structure fields constructors. If a struct has more fields, it will only get a default constructor.", new ArgDef(Type.Int, "fieldCount")),
195 CPlusPlusGen( "-genCPlusPlus", "[Experimental, Not working at all] Generate C++ classes.");
196
197 OptionDef(String clSwitch, String description, ArgDef... args) {
198 this.clSwitch = clSwitch;
199 this.description = description;
200 this.args = args;
201 switchPattern = clSwitch == null ? null : Pattern.compile(clSwitch);
202 for (int i = 0; i < args.length; i++)
203 args[i].position = i;
204 }
205 public String toString() {
206 return super.toString() + ": " + description;
207 }
208 public final ArgDef[] args;
209 public final Pattern switchPattern;
210 public final String clSwitch;
211 public final String description;
212
213 public enum Type {
214 ExistingFile, ExistingDir, File, String, Int, ExistingFileOrDir, OutputDir, OutputFile, OptionalFile
215 }
216 public static class ArgDef {
217 public final Type type;
218 public final String name;
219 public int position;
220 public ArgDef(Type type, String name) {
221 this.type = type;
222 this.name = name;
223 }
224 }
225 public ArgDef getParam(String name) {
226 for (ArgDef ad : args)
227 if (ad.name.equals(name))
228 return ad;
229 throw new NoSuchElementException("Argument parameter '" + name + "' in option " + this);
230 }
231
232 }
233
234 static void displayHelp(boolean wikiFormat) {
235 List<OptionDef> opts = new ArrayList<OptionDef>(Arrays.asList(OptionDef.values()));
236 Collections.sort(opts, new Comparator<OptionDef>() {
237 @Override
238 public int compare(OptionDef o1, OptionDef o2) {
239 if (o1.clSwitch == null)
240 return o2.clSwitch == null ? 0 : -1;
241 if (o2.clSwitch == null)
242 return 1;
243 return o1.clSwitch.compareTo(o2.clSwitch);
244 }
245
246 });
247 if (wikiFormat) {
248 for (OptionDef opt : opts) {
249 System.out.print(" * *" + (opt.clSwitch == null ? "" : opt.clSwitch) +"*");
250 for (OptionDef.ArgDef ad : opt.args) {
251 System.out.print(" <" + ad.name + ": " + ad.type + ">");
252 }
253 System.out.println();
254 System.out.println(" " + opt.description.replaceAll("\\*", "`*`").replaceAll("\n", "\n "));
255 }
256 } else {
257 System.out.println("Credits: JNAerator is Copyright (c) 2008-2009 Olivier Chafik");
258 System.out.println(" Includes Anarres JCPP (Apache 2.0 license), Copyright (c) 2007-2008, Shevek");
259 System.out.println(" Includes Java Native Access (JNA) (LGPL license), Copyright (c) 2006-2009 Todd Fast, Timothy Wall, Wayne Meissner and others");
260 System.out.println(" Includes Rococoa (LGPL license), Copyright (c) Copyright Duncan McGregor and others");
261 System.out.println(" Includes ANTLR's runtime (BSD license), Copyright (c) 2003-2008, Terence Parr");
262 System.out.println(" Licensing & Copyright details : http://code.google.com/p/jnaerator/wiki/CreditsAndLicense");
263
264 for (OptionDef opt : opts) {
265 System.out.print("\t" + (opt.clSwitch == null ? "" : opt.clSwitch));
266 for (OptionDef.ArgDef ad : opt.args) {
267 System.out.print(" <" + ad.name + ": " + ad.type + ">");
268 }
269 System.out.println();
270 System.out.println("\t\t" + opt.description);
271 System.out.println();
272 }
273 }
274 }
275 }