001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant.taskdefs.optional;
020:
021: import java.io.File;
022: import java.util.ArrayList;
023: import java.util.Enumeration;
024: import java.util.StringTokenizer;
025: import java.util.Vector;
026: import org.apache.tools.ant.BuildException;
027: import org.apache.tools.ant.Project;
028: import org.apache.tools.ant.Task;
029: import org.apache.tools.ant.taskdefs.optional.javah.JavahAdapter;
030: import org.apache.tools.ant.taskdefs.optional.javah.JavahAdapterFactory;
031: import org.apache.tools.ant.types.Commandline;
032: import org.apache.tools.ant.types.Path;
033: import org.apache.tools.ant.types.Reference;
034: import org.apache.tools.ant.util.facade.FacadeTaskHelper;
035: import org.apache.tools.ant.util.facade.ImplementationSpecificArgument;
036:
037: /**
038: * Generates JNI header files using javah.
039: *
040: * This task can take the following arguments:
041: * <ul>
042: * <li>classname - the fully-qualified name of a class</li>
043: * <li>outputFile - Concatenates the resulting header or source files for all
044: * the classes listed into this file</li>
045: * <li>destdir - Sets the directory where javah saves the header files or the
046: * stub files</li>
047: * <li>classpath</li>
048: * <li>bootclasspath</li>
049: * <li>force - Specifies that output files should always be written
050: (JDK1.2 only)</li>
051: * <li>old - Specifies that old JDK1.0-style header files should be generated
052: * (otherwise output file contain JNI-style native method
053: * function prototypes) (JDK1.2 only)</li>
054: * <li>stubs - generate C declarations from the Java object file (used with old)</li>
055: * <li>verbose - causes javah to print a message to stdout concerning the status
056: * of the generated files</li>
057: * <li>extdirs - Override location of installed extensions</li>
058: * </ul>
059: * Of these arguments, either <b>outputFile</b> or <b>destdir</b> is required,
060: * but not both. More than one classname may be specified, using a comma-separated
061: * list or by using <code><class name="xxx"></code> elements within the task.
062: * <p>
063: * When this task executes, it will generate C header and source files that
064: * are needed to implement native methods.
065: *
066: */
067:
068: public class Javah extends Task {
069:
070: private Vector classes = new Vector(2);
071: private String cls;
072: private File destDir;
073: private Path classpath = null;
074: private File outputFile = null;
075: private boolean verbose = false;
076: private boolean force = false;
077: private boolean old = false;
078: private boolean stubs = false;
079: private Path bootclasspath;
080: //private Path extdirs;
081: private static String lSep = System.getProperty("line.separator");
082: private FacadeTaskHelper facade = null;
083:
084: /**
085: * No arg constructor.
086: */
087: public Javah() {
088: facade = new FacadeTaskHelper(JavahAdapterFactory.getDefault());
089: }
090:
091: /**
092: * the fully-qualified name of the class (or classes, separated by commas).
093: * @param cls the classname (or classnames).
094: */
095: public void setClass(String cls) {
096: this .cls = cls;
097: }
098:
099: /**
100: * Adds class to process.
101: * @return a <code>ClassArgument</code> to be configured.
102: */
103: public ClassArgument createClass() {
104: ClassArgument ga = new ClassArgument();
105: classes.addElement(ga);
106: return ga;
107: }
108:
109: /**
110: * A class corresponding the the nested "class" element.
111: * It contains a "name" attribute.
112: */
113: public class ClassArgument {
114: private String name;
115:
116: /** Constructor for ClassArgument. */
117: public ClassArgument() {
118: }
119:
120: /**
121: * Set the name attribute.
122: * @param name the name attribute.
123: */
124: public void setName(String name) {
125: this .name = name;
126: }
127:
128: /**
129: * Get the name attribute.
130: * @return the name attribute.
131: */
132: public String getName() {
133: return name;
134: }
135: }
136:
137: /**
138: * Names of the classes to process.
139: * @return the array of classes.
140: * @since Ant 1.6.3
141: */
142: public String[] getClasses() {
143: ArrayList al = new ArrayList();
144: if (cls != null) {
145: StringTokenizer tok = new StringTokenizer(cls, ",", false);
146: while (tok.hasMoreTokens()) {
147: al.add(tok.nextToken().trim());
148: }
149: }
150:
151: Enumeration e = classes.elements();
152: while (e.hasMoreElements()) {
153: ClassArgument arg = (ClassArgument) e.nextElement();
154: al.add(arg.getName());
155: }
156: return (String[]) al.toArray(new String[al.size()]);
157: }
158:
159: /**
160: * Set the destination directory into which the Java source
161: * files should be compiled.
162: * @param destDir the destination directory.
163: */
164: public void setDestdir(File destDir) {
165: this .destDir = destDir;
166: }
167:
168: /**
169: * The destination directory, if any.
170: * @return the destination directory.
171: * @since Ant 1.6.3
172: */
173: public File getDestdir() {
174: return destDir;
175: }
176:
177: /**
178: * the classpath to use.
179: * @param src the classpath.
180: */
181: public void setClasspath(Path src) {
182: if (classpath == null) {
183: classpath = src;
184: } else {
185: classpath.append(src);
186: }
187: }
188:
189: /**
190: * Path to use for classpath.
191: * @return a path to be configured.
192: */
193: public Path createClasspath() {
194: if (classpath == null) {
195: classpath = new Path(getProject());
196: }
197: return classpath.createPath();
198: }
199:
200: /**
201: * Adds a reference to a classpath defined elsewhere.
202: * @param r a reference to a classpath.
203: * @todo this needs to be documented in the HTML docs.
204: */
205: public void setClasspathRef(Reference r) {
206: createClasspath().setRefid(r);
207: }
208:
209: /**
210: * The classpath to use.
211: * @return the classpath.
212: * @since Ant 1.6.3
213: */
214: public Path getClasspath() {
215: return classpath;
216: }
217:
218: /**
219: * location of bootstrap class files.
220: * @param src the bootstrap classpath.
221: */
222: public void setBootclasspath(Path src) {
223: if (bootclasspath == null) {
224: bootclasspath = src;
225: } else {
226: bootclasspath.append(src);
227: }
228: }
229:
230: /**
231: * Adds path to bootstrap class files.
232: * @return a path to be configured.
233: */
234: public Path createBootclasspath() {
235: if (bootclasspath == null) {
236: bootclasspath = new Path(getProject());
237: }
238: return bootclasspath.createPath();
239: }
240:
241: /**
242: * To the bootstrap path, this adds a reference to a classpath defined elsewhere.
243: * @param r a reference to a classpath
244: * @todo this needs to be documented in the HTML.
245: */
246: public void setBootClasspathRef(Reference r) {
247: createBootclasspath().setRefid(r);
248: }
249:
250: /**
251: * The bootclasspath to use.
252: * @return the bootclass path.
253: * @since Ant 1.6.3
254: */
255: public Path getBootclasspath() {
256: return bootclasspath;
257: }
258:
259: /**
260: * Concatenates the resulting header or source files for all
261: * the classes listed into this file.
262: * @param outputFile the output file.
263: */
264: public void setOutputFile(File outputFile) {
265: this .outputFile = outputFile;
266: }
267:
268: /**
269: * The destination file, if any.
270: * @return the destination file.
271: * @since Ant 1.6.3
272: */
273: public File getOutputfile() {
274: return outputFile;
275: }
276:
277: /**
278: * If true, output files should always be written (JDK1.2 only).
279: * @param force the value to use.
280: */
281: public void setForce(boolean force) {
282: this .force = force;
283: }
284:
285: /**
286: * Whether output files should always be written.
287: * @return the force attribute.
288: * @since Ant 1.6.3
289: */
290: public boolean getForce() {
291: return force;
292: }
293:
294: /**
295: * If true, specifies that old JDK1.0-style header files should be
296: * generated.
297: * (otherwise output file contain JNI-style native method function
298: * prototypes) (JDK1.2 only).
299: * @param old if true use old 1.0 style header files.
300: */
301: public void setOld(boolean old) {
302: this .old = old;
303: }
304:
305: /**
306: * Whether old JDK1.0-style header files should be generated.
307: * @return the old attribute.
308: * @since Ant 1.6.3
309: */
310: public boolean getOld() {
311: return old;
312: }
313:
314: /**
315: * If true, generate C declarations from the Java object file (used with old).
316: * @param stubs if true, generated C declarations.
317: */
318: public void setStubs(boolean stubs) {
319: this .stubs = stubs;
320: }
321:
322: /**
323: * Whether C declarations from the Java object file should be generated.
324: * @return the stubs attribute.
325: * @since Ant 1.6.3
326: */
327: public boolean getStubs() {
328: return stubs;
329: }
330:
331: /**
332: * If true, causes Javah to print a message concerning
333: * the status of the generated files.
334: * @param verbose if true, do verbose printing.
335: */
336: public void setVerbose(boolean verbose) {
337: this .verbose = verbose;
338: }
339:
340: /**
341: * Whether verbose output should get generated.
342: * @return the verbose attribute.
343: * @since Ant 1.6.3
344: */
345: public boolean getVerbose() {
346: return verbose;
347: }
348:
349: /**
350: * Choose the implementation for this particular task.
351: * @param impl the name of the implemenation.
352: * @since Ant 1.6.3
353: */
354: public void setImplementation(String impl) {
355: if ("default".equals(impl)) {
356: facade.setImplementation(JavahAdapterFactory.getDefault());
357: } else {
358: facade.setImplementation(impl);
359: }
360: }
361:
362: /**
363: * Adds an implementation specific command-line argument.
364: * @return a ImplementationSpecificArgument to be configured.
365: *
366: * @since Ant 1.6.3
367: */
368: public ImplementationSpecificArgument createArg() {
369: ImplementationSpecificArgument arg = new ImplementationSpecificArgument();
370: facade.addImplementationArgument(arg);
371: return arg;
372: }
373:
374: /**
375: * Returns the (implementation specific) settings given as nested
376: * arg elements.
377: * @return the arguments.
378: * @since Ant 1.6.3
379: */
380: public String[] getCurrentArgs() {
381: return facade.getArgs();
382: }
383:
384: /**
385: * Execute the task
386: *
387: * @throws BuildException is there is a problem in the task execution.
388: */
389: public void execute() throws BuildException {
390: // first off, make sure that we've got a srcdir
391:
392: if ((cls == null) && (classes.size() == 0)) {
393: throw new BuildException("class attribute must be set!",
394: getLocation());
395: }
396:
397: if ((cls != null) && (classes.size() > 0)) {
398: throw new BuildException(
399: "set class attribute or class element, "
400: + "not both.", getLocation());
401: }
402:
403: if (destDir != null) {
404: if (!destDir.isDirectory()) {
405: throw new BuildException("destination directory \""
406: + destDir
407: + "\" does not exist or is not a directory",
408: getLocation());
409: }
410: if (outputFile != null) {
411: throw new BuildException(
412: "destdir and outputFile are mutually "
413: + "exclusive", getLocation());
414: }
415: }
416:
417: if (classpath == null) {
418: classpath = (new Path(getProject()))
419: .concatSystemClasspath("last");
420: } else {
421: classpath = classpath.concatSystemClasspath("ignore");
422: }
423:
424: JavahAdapter ad = JavahAdapterFactory.getAdapter(facade
425: .getImplementation(), this );
426: if (!ad.compile(this )) {
427: throw new BuildException("compilation failed");
428: }
429: }
430:
431: /**
432: * Logs the compilation parameters, adds the files to compile and logs the
433: * "niceSourceList"
434: * @param cmd the command line.
435: */
436: public void logAndAddFiles(Commandline cmd) {
437: logAndAddFilesToCompile(cmd);
438: }
439:
440: /**
441: * Logs the compilation parameters, adds the files to compile and logs the
442: * "niceSourceList"
443: * @param cmd the command line to add parameters to.
444: */
445: protected void logAndAddFilesToCompile(Commandline cmd) {
446: log("Compilation " + cmd.describeArguments(),
447: Project.MSG_VERBOSE);
448:
449: StringBuffer niceClassList = new StringBuffer();
450: String[] c = getClasses();
451: for (int i = 0; i < c.length; i++) {
452: cmd.createArgument().setValue(c[i]);
453: niceClassList.append(" ");
454: niceClassList.append(c[i]);
455: niceClassList.append(lSep);
456: }
457:
458: StringBuffer prefix = new StringBuffer("Class");
459: if (c.length > 1) {
460: prefix.append("es");
461: }
462: prefix.append(" to be compiled:");
463: prefix.append(lSep);
464:
465: log(prefix.toString() + niceClassList.toString(),
466: Project.MSG_VERBOSE);
467: }
468: }
|