View Javadoc

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 		/// Returns a list that must be inserted in place of this argument
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 		//Test(				"-test",				"Launch JNAerator's unit tests (DEBUG option)"),
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 //		Undefine(			"-U(.*)?",				"Undefine a preprocessor symbol before ", new ArgDef(Type.String, "entryClassName")),
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 //		EnableCPlusPlus(	"-cppInstanceMethods",	"Enable experimental C++ instance methods wrapping"),
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 }