GlueGen v2.6.0-rc-20250712
GlueGen, Native Binding Generator for Java™ (public API).
GlueGenTask.java
Go to the documentation of this file.
1package com.jogamp.gluegen.ant;
2
3/*
4 * Copyright (C) 2003 Rob Grzywinski (rgrzywinski@realityinteractive.com)
5 * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
6 * Copyright (c) 2010 JogAmp Community. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * - Redistribution of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * - Redistribution in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * Neither the name of Sun Microsystems, Inc. or the names of
20 * contributors may be used to endorse or promote products derived from
21 * this software without specific prior written permission.
22 *
23 * This software is provided "AS IS," without a warranty of any kind. ALL
24 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
25 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
26 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
27 * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
28 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
29 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
30 * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
31 * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
32 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
33 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
34 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
35 *
36 * You acknowledge that this software is not designed or intended for use
37 * in the design, construction, operation or maintenance of any nuclear
38 * facility.
39 */
40
41import java.io.IOException;
42import java.util.Iterator;
43import java.util.LinkedList;
44import java.util.List;
45
46import org.apache.tools.ant.BuildException;
47import org.apache.tools.ant.DirectoryScanner;
48import org.apache.tools.ant.Project;
49import org.apache.tools.ant.Task;
50import org.apache.tools.ant.taskdefs.Execute;
51import org.apache.tools.ant.taskdefs.LogStreamHandler;
52import org.apache.tools.ant.types.AbstractFileSet;
53import org.apache.tools.ant.types.CommandlineJava;
54import org.apache.tools.ant.types.DirSet;
55import org.apache.tools.ant.types.FileSet;
56import org.apache.tools.ant.types.Path;
57import org.apache.tools.ant.types.PatternSet;
58import org.apache.tools.ant.types.Reference;
59import org.apache.tools.ant.util.JavaEnvUtils;
60
61/**
62 * <p>An <a href="http://ant.apache.org">ANT</a> {@link org.apache.tools.ant.Task}
63 * for using {@link com.jogamp.gluegen.GlueGen}.</p>
64 *
65 * <p>Usage:</p>
66 * <pre>
67 &lt;gluegen src="[source C file]"
68 outputrootdir="[optional output root dir]"
69 includes="[optional directory pattern of include files to include]"
70 excludes="[optional directory pattern of include files to exclude]"
71 includeRefid="[optional FileSet or DirSet for include files]"
72 literalInclude="[optional comma separated list of literal include directories, avoiding limitations of FileSet / DirSet issues]"
73 emitter="[emitter class name]"
74 config="[configuration file]"
75 dumpCPP="[optional boolean]"
76 debug="[optional boolean]"
77 logLevel="[optional string]" /&gt;
78 * </pre>
79 *
80 * @author Rob Grzywinski <a href="mailto:rgrzywinski@realityinteractive.com">rgrzywinski@yahoo.com</a>
81 */
82// FIXME: blow out javadoc
83// NOTE: this has not been exhaustively tested
84public class GlueGenTask extends Task
85{
86 /**
87 * <p>The {@link com.jogamp.gluegen.GlueGen} classname.</p>
88 */
89 private static final String GLUE_GEN = "com.jogamp.gluegen.GlueGen";
90
91 // =========================================================================
92 /**
93 * <p>The {@link org.apache.tools.ant.types.CommandlineJava} that is used
94 * to execute {@link com.jogamp.gluegen.GlueGen}.</p>
95 */
96 private final CommandlineJava gluegenCommandline;
97
98 // =========================================================================
99 /**
100 * <p>The optional debug flag.</p>
101 */
102 private boolean debug=false;
103
104 /**
105 * <p>The optional logLevel.</p>
106 */
107 private String logLevel = null;
108
109 /**
110 * <p>The optional dumpCPP flag.</p>
111 */
112 private boolean dumpCPP=false;
113
114 /**
115 * <p>The optional output root dir.</p>
116 */
117 private String outputRootDir;
118
119 /**
120 * <p>The name of the emitter class.</p>
121 */
122 private String emitter;
123
124 /**
125 * <p>The configuration file name.</p>
126 */
127 private String configuration;
128
129 /**
130 * <p>The name of the source C file that is to be parsed.</p>
131 */
132 private String sourceFile;
133
134 /**
135 * <p>The {@link org.apache.tools.ant.types.FileSet} of includes.</p>
136 */
137 private final FileSet includeSet = new FileSet();
138
139 /**
140 * <p>Because a {@link org.apache.tools.ant.types.FileSet} will include
141 * everything in its base directory if it is left untouched, the <code>includeSet</code>
142 * must only be added to the set of includes if it has been <i>explicitly</i>
143 * set.</p>
144 */
145 private boolean usedIncludeSet = false; // by default it is not used
146
147 /**
148 * <p>The set of include sets. This allows includes to be added in multiple
149 * fashions.</p>
150 */
151 // FIXME: rename to listXXXX
152 private final List<AbstractFileSet> setOfIncludeSets = new LinkedList<AbstractFileSet>();
153
154 /**
155 * <p>Comma separated list of literal directories to include. This is to get around the
156 * fact that neither {@link org.apache.tools.ant.types.FileSet} nor
157 * {@link org.apache.tools.ant.types.DirSet} can handle multiple drives in
158 * a sane manner or deal with relative path outside of the base-dir.
159 * If <code>null</code> then it has not been specified.</p>
160 */
161 private String literalIncludes;
162
163 // =========================================================================
164 /**
165 * <p>Create and add the VM and classname to {@link org.apache.tools.ant.types.CommandlineJava}.</p>
166 */
167 public GlueGenTask()
168 {
169 // create the CommandlineJava that will be used to call GlueGen
170 gluegenCommandline = new CommandlineJava();
171
172 // set the VM and classname in the commandline
173 gluegenCommandline.setVm(JavaEnvUtils.getJreExecutable("java"));
174 gluegenCommandline.setClassname(GLUE_GEN);
175 // gluegenCommandline.createVmArgument().setValue("-verbose:class");
176 }
177
178 // =========================================================================
179 // ANT getters and setters
180
181 /**
182 * <p>Set the debug flag (optional). This is called by ANT.</p>
183 */
184 public void setDebug(final boolean debug)
185 {
186 log( ("Setting debug flag: " + debug), Project.MSG_VERBOSE);
187 this.debug=debug;
188 }
189
190 /**
191 * <p>Set the logLevel (optional). This is called by ANT.</p>
192 */
193 public void setLogLevel(final String logLevel)
194 {
195 log( ("Setting logLevel: " + logLevel), Project.MSG_VERBOSE);
196 this.logLevel=logLevel;
197 }
198
199 /**
200 * <p>Set the dumpCPP flag (optional). This is called by ANT.</p>
201 */
202 public void setDumpCPP(final boolean dumpCPP)
203 {
204 log( ("Setting dumpCPP flag: " + dumpCPP), Project.MSG_VERBOSE);
205 this.dumpCPP=dumpCPP;
206 }
207
208 /**
209 * <p>Set the output root dir (optional). This is called by ANT.</p>
210 *
211 * @param outputRootDir the optional output root dir
212 */
213 public void setOutputRootDir(final String outputRootDir)
214 {
215 log( ("Setting output root dir: " + outputRootDir), Project.MSG_VERBOSE);
216 this.outputRootDir=outputRootDir;
217 }
218
219 /**
220 * <p>Set the emitter class name. This is called by ANT.</p>
221 *
222 * @param emitter the name of the emitter class
223 */
224 public void setEmitter(final String emitter)
225 {
226 log( ("Setting emitter class name to: " + emitter), Project.MSG_VERBOSE);
227 this.emitter = emitter;
228 }
229
230 /**
231 * <p>Set the configuration file name. This is called by ANT.</p>
232 *
233 * @param configuration the name of the configuration file
234 */
235 public void setConfig(final String configuration)
236 {
237 log( ("Setting configuration file name to: " + configuration),
238 Project.MSG_VERBOSE);
239 this.configuration = configuration;
240 }
241
242 /**
243 * <p>Set the source C file that is to be parsed. This is called by ANT.</p>
244 *
245 * @param sourceFile the name of the source file
246 */
247 public void setSrc(final String sourceFile)
248 {
249 log( ("Setting source file name to: " + sourceFile), Project.MSG_VERBOSE);
250 this.sourceFile = sourceFile;
251 }
252
253 /**
254 * <p>Set a literal include directories, separated with a comma. See the <code>literalInclude</code>
255 * javadoc for more information.</p>
256 *
257 * @param commaSeparatedIncludes the comma separated directories to include
258 */
259 public void setLiteralInclude(final String commaSeparatedIncludes)
260 {
261 this.literalIncludes = commaSeparatedIncludes.trim();
262 }
263
264 /**
265 * <p>Add an include file to the list. This is called by ANT for a nested
266 * element.</p>
267 *
268 * @return {@link org.apache.tools.ant.types.PatternSet.NameEntry}
269 */
270 public PatternSet.NameEntry createInclude()
271 {
272 usedIncludeSet = true;
273 return includeSet.createInclude();
274 }
275
276 /**
277 * <p>Add an include file to the list. This is called by ANT for a nested
278 * element.</p>
279 *
280 * @return {@link org.apache.tools.ant.types.PatternSet.NameEntry}
281 */
282 public PatternSet.NameEntry createIncludesFile()
283 {
284 usedIncludeSet = true;
285 return includeSet.createIncludesFile();
286 }
287
288 /**
289 * <p>Set the set of include patterns. Patterns may be separated by a comma
290 * or a space. This is called by ANT.</p>
291 *
292 * @param includes the string containing the include patterns
293 */
294 public void setIncludes(final String includes)
295 {
296 usedIncludeSet = true;
297 includeSet.setIncludes(includes);
298 }
299
300 /**
301 * <p>Add an include file to the list that is to be exluded. This is called
302 * by ANT for a nested element.</p>
303 *
304 * @return {@link org.apache.tools.ant.types.PatternSet.NameEntry}
305 */
306 public PatternSet.NameEntry createExclude()
307 {
308 usedIncludeSet = true;
309 return includeSet.createExclude();
310 }
311
312 /**
313 * <p>Add an exclude file to the list. This is called by ANT for a nested
314 * element.</p>
315 *
316 * @return {@link org.apache.tools.ant.types.PatternSet.NameEntry}
317 */
318 public PatternSet.NameEntry createExcludesFile()
319 {
320 usedIncludeSet = true;
321 return includeSet.createExcludesFile();
322 }
323
324 /**
325 * <p>Set the set of exclude patterns. Patterns may be separated by a comma
326 * or a space. This is called by ANT.</p>
327 *
328 * @param includes the string containing the exclude patterns
329 */
330 public void setExcludes(final String excludes)
331 {
332 usedIncludeSet = true;
333 includeSet.setExcludes(excludes);
334 }
335
336 /**
337 * <p>Set a {@link org.apache.tools.ant.types.Reference} to simplify adding
338 * of complex sets of files to include. This is called by ANT.</p>?
339 *
340 * @param reference a <code>Reference</code> to a {@link org.apache.tools.ant.types.FileSet}
341 * or {@link org.apache.tools.ant.types.DirSet}
342 * @throws BuildException if the specified <code>Reference</code> is not
343 * either a <code>FileSet</code> or <code>DirSet</code>
344 */
345public void setIncludeRefid(final Reference reference) {
346 // ensure that the referenced object is either a FileSet or DirSet
347 final Object referencedObject = reference.getReferencedObject(getProject());
348 if (referencedObject instanceof FileSet) {
349 setOfIncludeSets.add((FileSet)referencedObject);
350 return;
351 }
352 if (referencedObject instanceof DirSet) {
353 setOfIncludeSets.add((DirSet)referencedObject);
354 return;
355 }
356
357 throw new BuildException("Only FileSets or DirSets are allowed as an include refid.");
358}
359
360 /**
361 * <p>Add a nested {@link org.apache.tools.ant.types.DirSet} to specify
362 * the files to include. This is called by ANT.</p>
363 *
364 * @param dirset the <code>DirSet</code> to be added
365 */
366 public void addDirset(final DirSet dirset)
367 {
368 setOfIncludeSets.add(dirset);
369 }
370
371 /**
372 * <p>Add an optional classpath that defines the location of {@link com.jogamp.gluegen.GlueGen}
373 * and <code>GlueGen</code>'s dependencies.</p>
374 *
375 * @returns {@link org.apache.tools.ant.types.Path}
376 */
377 public Path createClasspath()
378 {
379 return gluegenCommandline.createClasspath(project).createPath();
380 }
381
382 // =========================================================================
383 /**
384 * <p>Run the task. This involves validating the set attributes, creating
385 * the command line to be executed and finally executing the command.</p>
386 *
387 * @see org.apache.tools.ant.Task#execute()
388 */
389 @Override
390 public void execute()
391 throws BuildException
392 {
393 // validate that all of the required attributes have been set
394 validateAttributes();
395
396 // TODO: add logic to determine if the generated file needs to be
397 // regenerated
398
399 // add the attributes to the CommandlineJava
400 addAttributes();
401
402 log(gluegenCommandline.describeCommand(), Project.MSG_VERBOSE);
403
404 // execute the command and throw on error
405 final int error = execute(gluegenCommandline.getCommandline());
406 if(error == 1)
407 throw new BuildException( ("GlueGen returned: " + error), location);
408 }
409
410 /**
411 * <p>Ensure that the user specified all required arguments.</p>
412 *
413 * @throws BuildException if there are required arguments that are not
414 * present or not valid
415 */
416 private void validateAttributes()
417 throws BuildException
418 {
419 // outputRootDir is optional ..
420
421 // validate that the emitter class is set
422 if(!isValid(emitter))
423 throw new BuildException("Invalid emitter class name: " + emitter);
424
425 // validate that the configuration file is set
426 if(!isValid(configuration))
427 throw new BuildException("Invalid configuration file name: " + configuration);
428
429 // validate that the source file is set
430 if(!isValid(sourceFile))
431 throw new BuildException("Invalid source file name: " + sourceFile);
432
433 // CHECK: do there need to be includes to be valid?
434 }
435
436 /**
437 * <p>Is the specified string valid? A valid string is non-<code>null</code>
438 * and has a non-zero length.</p>
439 *
440 * @param string the string to be tested for validity
441 * @return <code>true</code> if the string is valid. <code>false</code>
442 * otherwise.
443 */
444 private boolean isValid(final String string)
445 {
446 // check for null
447 if(string == null)
448 return false;
449
450 // ensure that the string has a non-zero length
451 // NOTE: must trim() to remove leading and trailing whitespace
452 if(string.trim().length() < 1)
453 return false;
454
455 // the string is valid
456 return true;
457 }
458
459 /**
460 * <p>Add all of the attributes to the command line. They have already
461 * been validated.</p>
462 */
463 private void addAttributes()
464 throws BuildException
465 {
466 // NOTE: GlueGen uses concatenated flag / value rather than two
467 // separate arguments
468
469 // add the debug flag if enabled
470 if(debug) {
471 gluegenCommandline.createArgument().setValue("--debug");
472 }
473
474 // add the logLevel if enabled
475 if(null != logLevel) {
476 gluegenCommandline.createArgument().setValue("--logLevel");
477 gluegenCommandline.createArgument().setValue(logLevel);
478 }
479
480 // add the debug flag if enabled
481 if(dumpCPP) {
482 gluegenCommandline.createArgument().setValue("--dumpCPP");
483 }
484
485 // add the output root dir
486 if(null!=outputRootDir && outputRootDir.trim().length()>0) {
487 gluegenCommandline.createArgument().setValue("-O" + outputRootDir);
488 }
489
490 // add the emitter class name
491 gluegenCommandline.createArgument().setValue("-E" + emitter);
492
493 // add the configuration file name
494 gluegenCommandline.createArgument().setValue("-C" + configuration);
495
496 // add the includedSet to the setOfIncludeSets to simplify processing
497 // all types of include sets ONLY if it has been set.
498 // NOTE: see the usedIncludeSet member javadoc for more info
499 // NOTE: references and nested DirSets have already been added to the
500 // set of include sets
501 if(usedIncludeSet)
502 {
503 includeSet.setDir(getProject().getBaseDir()); // NOTE: the base dir must be set
504 setOfIncludeSets.add(includeSet);
505 }
506
507 // iterate over all include sets and add their directories to the
508 // list of included directories.
509 final List<String> includedDirectories = new LinkedList<String>();
510 for (final Iterator<AbstractFileSet> includes = setOfIncludeSets.iterator(); includes.hasNext();)
511 {
512 // get the included set and based on its type add the directories
513 // to includedDirectories
514 final AbstractFileSet include = includes.next();
515 final DirectoryScanner directoryScanner = include.getDirectoryScanner(getProject());
516 final String[] directoryDirs = directoryScanner.getIncludedDirectories();
517
518 // add the directoryDirs to the includedDirectories
519 // TODO: exclude any directory that is already in the list
520 for(int i=0; i<directoryDirs.length; i++)
521 {
522 includedDirectories.add(directoryDirs[i]);
523 }
524 }
525
526 // if literalInclude is valid then add it to the list of included
527 // directories
528 if( isValid( literalIncludes ) ) {
529 final String[] includes = literalIncludes.split(",");
530 for(int i=0; i<includes.length; i++) {
531 final String include = includes[i].trim();
532 if( include.length()>0 ) {
533 includedDirectories.add(include);
534 }
535 }
536 }
537
538 // add the included directories to the command
539 for(final Iterator<String> includes=includedDirectories.iterator(); includes.hasNext(); )
540 {
541 final String directory = includes.next();
542 gluegenCommandline.createArgument().setValue("-I" + directory);
543 }
544
545 // finally, add the source file
546 gluegenCommandline.createArgument().setValue(sourceFile);
547 }
548
549 /**
550 * <p>Execute {@link com.jogamp.gluegen.GlueGen} in a forked JVM.</p>
551 *
552 * @throws BuildException
553 */
554 private int execute(final String[] command)
555 throws BuildException
556 {
557 // create the object that will perform the command execution
558 final Execute execute = new Execute(new LogStreamHandler(this, Project.MSG_INFO,
559 Project.MSG_WARN),
560 null);
561
562 // set the project and command line
563 execute.setAntRun(project);
564 execute.setCommandline(command);
565 execute.setWorkingDirectory( project.getBaseDir() );
566
567 // execute the command
568 try
569 {
570 return execute.execute();
571 } catch(final IOException ioe)
572 {
573 throw new BuildException(ioe, location);
574 }
575 }
576}
void setIncludes(final String includes)
PatternSet.NameEntry createIncludesFile()
void setLiteralInclude(final String commaSeparatedIncludes)
PatternSet.NameEntry createInclude()
PatternSet.NameEntry createExclude()
void setDumpCPP(final boolean dumpCPP)
void setExcludes(final String excludes)
void setOutputRootDir(final String outputRootDir)
void setSrc(final String sourceFile)
void setConfig(final String configuration)
void setEmitter(final String emitter)
void addDirset(final DirSet dirset)
void setLogLevel(final String logLevel)
void setDebug(final boolean debug)
void setIncludeRefid(final Reference reference)
PatternSet.NameEntry createExcludesFile()