1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18 package org.apache.tools.ant.taskdefs;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.rmi.Remote;
23 import java.util.Vector;
24 import org.apache.tools.ant.AntClassLoader;
25 import org.apache.tools.ant.BuildException;
26 import org.apache.tools.ant.DirectoryScanner;
27 import org.apache.tools.ant.Project;
28 import org.apache.tools.ant.taskdefs.rmic.RmicAdapter;
29 import org.apache.tools.ant.taskdefs.rmic.RmicAdapterFactory;
30 import org.apache.tools.ant.types.FilterSetCollection;
31 import org.apache.tools.ant.types.Path;
32 import org.apache.tools.ant.types.Reference;
33 import org.apache.tools.ant.util.FileNameMapper;
34 import org.apache.tools.ant.util.FileUtils;
35 import org.apache.tools.ant.util.SourceFileScanner;
36 import org.apache.tools.ant.util.StringUtils;
37 import org.apache.tools.ant.util.facade.FacadeTaskHelper;
38
39 /**
40 * Runs the rmic compiler against classes.</p>
41 * <p>Rmic can be run on a single class (as specified with the classname
42 * attribute) or a number of classes at once (all classes below base that
43 * are neither _Stub nor _Skel classes). If you want to rmic a single
44 * class and this class is a class nested into another class, you have to
45 * specify the classname in the form <code>Outer$$Inner</code> instead of
46 * <code>Outer.Inner</code>.</p>
47 * <p>It is possible to refine the set of files that are being rmiced. This can
48 * be done with the <i>includes</i>, <i>includesfile</i>, <i>excludes</i>,
49 * <i>excludesfile</i> and <i>defaultexcludes</i>
50 * attributes. With the <i>includes</i> or <i>includesfile</i> attribute you
51 * specify the files you want to have included by using patterns. The
52 * <i>exclude</i> or <i>excludesfile</i> attribute is used to specify
53 * the files you want to have excluded. This is also done with patterns. And
54 * finally with the <i>defaultexcludes</i> attribute, you can specify whether
55 * you want to use default exclusions or not. See the section on
56 * directory based tasks</a>, on how the
57 * inclusion/exclusion of files works, and how to write patterns.</p>
58 * <p>This task forms an implicit FileSet and
59 * supports all attributes of <code><fileset></code>
60 * (<code>dir</code> becomes <code>base</code>) as well as the nested
61 * <code><include></code>, <code><exclude></code> and
62 * <code><patternset></code> elements.</p>
63 * <p>It is possible to use different compilers. This can be selected
64 * with the "build.rmic" property or the <code>compiler</code>
65 * attribute. <a name="compilervalues">There are three choices</a>:</p>
66 * <ul>
67 * <li>sun (the standard compiler of the JDK)</li>
68 * <li>kaffe (the standard compiler of
69 * {@link <a href="http://www.kaffe.org">Kaffe</a>})</li>
70 * <li>weblogic</li>
71 * </ul>
72 *
73 * <p> The <a href="http://dione.zcu.cz/~toman40/miniRMI/">miniRMI</a>
74 * project contains a compiler implementation for this task as well,
75 * please consult miniRMI's documentation to learn how to use it.</p>
76 *
77 * @since Ant 1.1
78 *
79 * @ant.task category="java"
80 */
81
82 public class Rmic extends MatchingTask {
83
84 /** rmic failed message */
85 public static final String ERROR_RMIC_FAILED
86 = "Rmic failed; see the compiler error output for details.";
87
88 private File baseDir;
89 private File destDir;
90 private String classname;
91 private File sourceBase;
92 private String stubVersion;
93 private Path compileClasspath;
94 private Path extDirs;
95 private boolean verify = false;
96 private boolean filtering = false;
97
98 private boolean iiop = false;
99 private String iiopOpts;
100 private boolean idl = false;
101 private String idlOpts;
102 private boolean debug = false;
103 private boolean includeAntRuntime = true;
104 private boolean includeJavaRuntime = false;
105
106 private Vector compileList = new Vector();
107
108 private AntClassLoader loader = null;
109
110 private FacadeTaskHelper facade;
111 /** unable to verify message */
112 public static final String ERROR_UNABLE_TO_VERIFY_CLASS = "Unable to verify class ";
113 /** could not be found message */
114 public static final String ERROR_NOT_FOUND = ". It could not be found.";
115 /** not defined message */
116 public static final String ERROR_NOT_DEFINED = ". It is not defined.";
117 /** loaded error message */
118 public static final String ERROR_LOADING_CAUSED_EXCEPTION = ". Loading caused Exception: ";
119 /** base not exists message */
120 public static final String ERROR_NO_BASE_EXISTS = "base or destdir does not exist: ";
121 /** base not a directory message */
122 public static final String ERROR_NOT_A_DIR = "base or destdir is not a directory:";
123 /** base attribute not set message */
124 public static final String ERROR_BASE_NOT_SET = "base or destdir attribute must be set!";
125
126 private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
127
128 private String executable = null;
129
130 private boolean listFiles = false;
131
132 private RmicAdapter nestedAdapter = null;
133
134 /**
135 * Constructor for Rmic.
136 */
137 public Rmic() {
138 facade = new FacadeTaskHelper(RmicAdapterFactory.DEFAULT_COMPILER);
139 }
140
141 /**
142 * Sets the location to store the compiled files; required
143 * @param base the location to store the compiled files
144 */
145 public void setBase(File base) {
146 this.baseDir = base;
147 }
148
149 /**
150 * Sets the base directory to output the generated files.
151 * @param destdir the base directory to output the generated files.
152 * @since Ant 1.8.0
153 */
154 public void setDestdir(File destdir) {
155 this.destDir = destdir;
156 }
157
158 /**
159 * Gets the base directory to output the generated files.
160 * @return the base directory to output the generated files.
161 * @since Ant 1.8.0
162 */
163 public File getDestdir() {
164 return this.destDir;
165 }
166
167 /**
168 * Gets the base directory to output the generated files,
169 * favoring destdir if set, otherwise defaulting to basedir.
170 * @return the actual directory to output to (either destdir or basedir)
171 * @since Ant 1.8.0
172 */
173 public File getOutputDir() {
174 if (getDestdir() != null) {
175 return getDestdir();
176 }
177 return getBase();
178 }
179
180 /**
181 * Gets the base directory to output generated class.
182 * @return the location of the compiled files
183 */
184 public File getBase() {
185 return this.baseDir;
186 }
187
188 /**
189 * Sets the class to run <code>rmic</code> against;
190 * optional
191 * @param classname the name of the class for rmic to create code for
192 */
193 public void setClassname(String classname) {
194 this.classname = classname;
195 }
196
197 /**
198 * Gets the class name to compile.
199 * @return the name of the class to compile
200 */
201 public String getClassname() {
202 return classname;
203 }
204
205 /**
206 * optional directory to save generated source files to.
207 * @param sourceBase the directory to save source files to.
208 */
209 public void setSourceBase(File sourceBase) {
210 this.sourceBase = sourceBase;
211 }
212
213 /**
214 * Gets the source dirs to find the source java files.
215 * @return sourceBase the directory containing the source files.
216 */
217 public File getSourceBase() {
218 return sourceBase;
219 }
220
221 /**
222 * Specify the JDK version for the generated stub code.
223 * Specify "1.1" to pass the "-v1.1" option to rmic.
224 * @param stubVersion the JDK version
225 */
226 public void setStubVersion(String stubVersion) {
227 this.stubVersion = stubVersion;
228 }
229
230 /**
231 * Gets the JDK version for the generated stub code.
232 * @return stubVersion
233 */
234 public String getStubVersion() {
235 return stubVersion;
236 }
237
238 /**
239 * Sets token filtering [optional], default=false
240 * @param filter turn on token filtering
241 */
242 public void setFiltering(boolean filter) {
243 this.filtering = filter;
244 }
245
246 /**
247 * Gets whether token filtering is set
248 * @return filtering
249 */
250 public boolean getFiltering() {
251 return filtering;
252 }
253
254 /**
255 * Generate debug info (passes -g to rmic);
256 * optional, defaults to false
257 * @param debug turn on debug info
258 */
259 public void setDebug(boolean debug) {
260 this.debug = debug;
261 }
262
263 /**
264 * Gets the debug flag.
265 * @return debug
266 */
267 public boolean getDebug() {
268 return debug;
269 }
270
271 /**
272 * Set the classpath to be used for this compilation.
273 * @param classpath the classpath used for this compilation
274 */
275 public synchronized void setClasspath(Path classpath) {
276 if (compileClasspath == null) {
277 compileClasspath = classpath;
278 } else {
279 compileClasspath.append(classpath);
280 }
281 }
282
283 /**
284 * Creates a nested classpath element.
285 * @return classpath
286 */
287 public synchronized Path createClasspath() {
288 if (compileClasspath == null) {
289 compileClasspath = new Path(getProject());
290 }
291 return compileClasspath.createPath();
292 }
293
294 /**
295 * Adds to the classpath a reference to
296 * a <path> defined elsewhere.
297 * @param pathRef the reference to add to the classpath
298 */
299 public void setClasspathRef(Reference pathRef) {
300 createClasspath().setRefid(pathRef);
301 }
302
303 /**
304 * Gets the classpath.
305 * @return the classpath
306 */
307 public Path getClasspath() {
308 return compileClasspath;
309 }
310
311 /**
312 * Flag to enable verification so that the classes
313 * found by the directory match are
314 * checked to see if they implement java.rmi.Remote.
315 * optional; This defaults to false if not set.
316 * @param verify turn on verification for classes
317 */
318 public void setVerify(boolean verify) {
319 this.verify = verify;
320 }
321
322 /**
323 * Get verify flag.
324 * @return verify
325 */
326 public boolean getVerify() {
327 return verify;
328 }
329
330 /**
331 * Indicates that IIOP compatible stubs should
332 * be generated; optional, defaults to false
333 * if not set.
334 * @param iiop generate IIOP compatible stubs
335 */
336 public void setIiop(boolean iiop) {
337 this.iiop = iiop;
338 }
339
340 /**
341 * Gets iiop flags.
342 * @return iiop
343 */
344 public boolean getIiop() {
345 return iiop;
346 }
347
348 /**
349 * Set additional arguments for iiop
350 * @param iiopOpts additional arguments for iiop
351 */
352 public void setIiopopts(String iiopOpts) {
353 this.iiopOpts = iiopOpts;
354 }
355
356 /**
357 * Gets additional arguments for iiop.
358 * @return iiopOpts
359 */
360 public String getIiopopts() {
361 return iiopOpts;
362 }
363
364 /**
365 * Indicates that IDL output should be
366 * generated. This defaults to false
367 * if not set.
368 * @param idl generate IDL output
369 */
370 public void setIdl(boolean idl) {
371 this.idl = idl;
372 }
373
374 /**
375 * Gets IDL flags.
376 * @return the idl flag
377 */
378 public boolean getIdl() {
379 return idl;
380 }
381
382 /**
383 * pass additional arguments for IDL compile
384 * @param idlOpts additional IDL arguments
385 */
386 public void setIdlopts(String idlOpts) {
387 this.idlOpts = idlOpts;
388 }
389
390 /**
391 * Gets additional arguments for idl compile.
392 * @return the idl options
393 */
394 public String getIdlopts() {
395 return idlOpts;
396 }
397
398 /**
399 * Gets file list to compile.
400 * @return the list of files to compile.
401 */
402 public Vector getFileList() {
403 return compileList;
404 }
405
406 /**
407 * Sets whether or not to include ant's own classpath in this task's
408 * classpath.
409 * Optional; default is <code>true</code>.
410 * @param include if true include ant's classpath
411 */
412 public void setIncludeantruntime(boolean include) {
413 includeAntRuntime = include;
414 }
415
416 /**
417 * Gets whether or not the ant classpath is to be included in the
418 * task's classpath.
419 * @return true if ant's classpath is to be included
420 */
421 public boolean getIncludeantruntime() {
422 return includeAntRuntime;
423 }
424
425 /**
426 * task's classpath.
427 * Enables or disables including the default run-time
428 * libraries from the executing VM; optional,
429 * defaults to false
430 * @param include if true include default run-time libraries
431 */
432 public void setIncludejavaruntime(boolean include) {
433 includeJavaRuntime = include;
434 }
435
436 /**
437 * Gets whether or not the java runtime should be included in this
438 * task's classpath.
439 * @return true if default run-time libraries are included
440 */
441 public boolean getIncludejavaruntime() {
442 return includeJavaRuntime;
443 }
444
445 /**
446 * Sets the extension directories that will be used during the
447 * compilation; optional.
448 * @param extDirs the extension directories to be used
449 */
450 public synchronized void setExtdirs(Path extDirs) {
451 if (this.extDirs == null) {
452 this.extDirs = extDirs;
453 } else {
454 this.extDirs.append(extDirs);
455 }
456 }
457
458 /**
459 * Maybe creates a nested extdirs element.
460 * @return path object to be configured with the extension directories
461 */
462 public synchronized Path createExtdirs() {
463 if (extDirs == null) {
464 extDirs = new Path(getProject());
465 }
466 return extDirs.createPath();
467 }
468
469 /**
470 * Gets the extension directories that will be used during the
471 * compilation.
472 * @return the extension directories to be used
473 */
474 public Path getExtdirs() {
475 return extDirs;
476 }
477
478 /**
479 * @return the compile list.
480 */
481 public Vector getCompileList() {
482 return compileList;
483 }
484
485 /**
486 * Sets the compiler implementation to use; optional,
487 * defaults to the value of the <code>build.rmic</code> property,
488 * or failing that, default compiler for the current VM
489 * @param compiler the compiler implemention to use
490 * @since Ant 1.5
491 */
492 public void setCompiler(String compiler) {
493 if (compiler.length() > 0) {
494 facade.setImplementation(compiler);
495 }
496 }
497
498 /**
499 * get the name of the current compiler
500 * @return the name of the compiler
501 * @since Ant 1.5
502 */
503 public String getCompiler() {
504 facade.setMagicValue(getProject().getProperty("build.rmic"));
505 return facade.getImplementation();
506 }
507
508 /**
509 * Adds an implementation specific command line argument.
510 * @return an object to be configured with a command line argument
511 * @since Ant 1.5
512 */
513 public ImplementationSpecificArgument createCompilerArg() {
514 ImplementationSpecificArgument arg = new ImplementationSpecificArgument();
515 facade.addImplementationArgument(arg);
516 return arg;
517 }
518
519 /**
520 * Get the additional implementation specific command line arguments.
521 * @return array of command line arguments, guaranteed to be non-null.
522 * @since Ant 1.5
523 */
524 public String[] getCurrentCompilerArgs() {
525 getCompiler();
526 return facade.getArgs();
527 }
528
529 /**
530 * Name of the executable to use when forking.
531 *
532 * @since Ant 1.8.0
533 */
534 public void setExecutable(String ex) {
535 executable = ex;
536 }
537
538 /**
539 * Explicitly specified name of the executable to use when forking
540 * - if any.
541 *
542 * @since Ant 1.8.0
543 */
544 public String getExecutable() {
545 return executable;
546 }
547
548 /**
549 * The classpath to use when loading the compiler implementation
550 * if it is not a built-in one.
551 *
552 * @since Ant 1.8.0
553 */
554 public Path createCompilerClasspath() {
555 return facade.getImplementationClasspath(getProject());
556 }
557
558 /**
559 * If true, list the source files being handed off to the compiler.
560 * @param list if true list the source files
561 * @since Ant 1.8.0
562 */
563 public void setListfiles(boolean list) {
564 listFiles = list;
565 }
566
567 /**
568 * Set the compiler adapter explicitly.
569 * @since Ant 1.8.0
570 */
571 public void add(RmicAdapter adapter) {
572 if (nestedAdapter != null) {
573 throw new BuildException("Can't have more than one rmic adapter");
574 }
575 nestedAdapter = adapter;
576 }
577
578 /**
579 * execute by creating an instance of an implementation
580 * class and getting to do the work
581 * @throws org.apache.tools.ant.BuildException
582 * if there's a problem with baseDir or RMIC
583 */
584 public void execute() throws BuildException {
585 try {
586 compileList.clear();
587
588 File outputDir = getOutputDir();
589 if (outputDir == null) {
590 throw new BuildException(ERROR_BASE_NOT_SET, getLocation());
591 }
592 if (!outputDir.exists()) {
593 throw new BuildException(ERROR_NO_BASE_EXISTS + outputDir,
594 getLocation());
595 }
596 if (!outputDir.isDirectory()) {
597 throw new BuildException(ERROR_NOT_A_DIR + outputDir, getLocation());
598 }
599 if (verify) {
600 log("Verify has been turned on.", Project.MSG_VERBOSE);
601 }
602 RmicAdapter adapter =
603 nestedAdapter != null ? nestedAdapter :
604 RmicAdapterFactory.getRmic(getCompiler(), this,
605 createCompilerClasspath());
606
607 // now we need to populate the compiler adapter
608 adapter.setRmic(this);
609
610 Path classpath = adapter.getClasspath();
611 loader = getProject().createClassLoader(classpath);
612
613 // scan base dirs to build up compile lists only if a
614 // specific classname is not given
615 if (classname == null) {
616 DirectoryScanner ds = this.getDirectoryScanner(baseDir);
617 String[] files = ds.getIncludedFiles();
618 scanDir(baseDir, files, adapter.getMapper());
619 } else {
620 // otherwise perform a timestamp comparison - at least
621 String path = classname.replace('.', File.separatorChar)
622 + ".class";
623 File f = new File(baseDir, path);
624 if (f.isFile()) {
625 scanDir(baseDir, new String[] {path}, adapter.getMapper());
626 } else {
627 // Does not exist, so checking whether it is up to
628 // date makes no sense. Compilation will fail
629 // later anyway, but tests expect a certain
630 // output.
631 compileList.add(classname);
632 }
633 }
634 int fileCount = compileList.size();
635 if (fileCount > 0) {
636 log("RMI Compiling " + fileCount + " class"
637 + (fileCount > 1 ? "es" : "") + " to "
638 + outputDir, Project.MSG_INFO);
639
640 if (listFiles) {
641 for (int i = 0; i < fileCount; i++) {
642 log(compileList.get(i).toString());
643 }
644 }
645
646 // finally, lets execute the compiler!!
647 if (!adapter.execute()) {
648 throw new BuildException(ERROR_RMIC_FAILED, getLocation());
649 }
650 }
651 /*
652 * Move the generated source file to the base directory. If
653 * base directory and sourcebase are the same, the generated
654 * sources are already in place.
655 */
656 if (null != sourceBase && !outputDir.equals(sourceBase)
657 && fileCount > 0) {
658 if (idl) {
659 log("Cannot determine sourcefiles in idl mode, ",
660 Project.MSG_WARN);
661 log("sourcebase attribute will be ignored.",
662 Project.MSG_WARN);
663 } else {
664 for (int j = 0; j < fileCount; j++) {
665 moveGeneratedFile(outputDir, sourceBase,
666 (String) compileList.elementAt(j),
667 adapter);
668 }
669 }
670 }
671 } finally {
672 cleanup();
673 }
674 }
675
676 /**
677 * Cleans up resources.
678 *
679 * @since Ant 1.8.0
680 */
681 protected void cleanup() {
682 if (loader != null) {
683 loader.cleanup();
684 loader = null;
685 }
686 }
687
688 /**
689 * Move the generated source file(s) to the base directory
690 *
691 * @throws org.apache.tools.ant.BuildException When error
692 * copying/removing files.
693 */
694 private void moveGeneratedFile(File baseDir, File sourceBaseFile, String classname,
695 RmicAdapter adapter) throws BuildException {
696 String classFileName = classname.replace('.', File.separatorChar)
697 + ".class";
698 String[] generatedFiles = adapter.getMapper().mapFileName(classFileName);
699
700 for (int i = 0; i < generatedFiles.length; i++) {
701 final String generatedFile = generatedFiles[i];
702 if (!generatedFile.endsWith(".class")) {
703 // don't know how to handle that - a IDL file doesn't
704 // have a corresponding Java source for example.
705 continue;
706 }
707 String sourceFileName = StringUtils.removeSuffix(generatedFile,
708 ".class")
709 + ".java";
710
711 File oldFile = new File(baseDir, sourceFileName);
712 if (!oldFile.exists()) {
713 // no source file generated, nothing to move
714 continue;
715 }
716
717 File newFile = new File(sourceBaseFile, sourceFileName);
718 try {
719 if (filtering) {
720 FILE_UTILS.copyFile(oldFile, newFile,
721 new FilterSetCollection(getProject()
722 .getGlobalFilterSet()));
723 } else {
724 FILE_UTILS.copyFile(oldFile, newFile);
725 }
726 oldFile.delete();
727 } catch (IOException ioe) {
728 String msg = "Failed to copy " + oldFile + " to " + newFile
729 + " due to "
730 + ioe.getMessage();
731 throw new BuildException(msg, ioe, getLocation());
732 }
733 }
734 }
735
736 /**
737 * Scans the directory looking for class files to be compiled.
738 * The result is returned in the class variable compileList.
739 * @param baseDir the base direction
740 * @param files the list of files to scan
741 * @param mapper the mapper of files to target files
742 */
743 protected void scanDir(File baseDir, String[] files, FileNameMapper mapper) {
744 String[] newFiles = files;
745 if (idl) {
746 log("will leave uptodate test to rmic implementation in idl mode.",
747 Project.MSG_VERBOSE);
748 } else if (iiop && iiopOpts != null && iiopOpts.indexOf("-always") > -1) {
749 log("no uptodate test as -always option has been specified",
750 Project.MSG_VERBOSE);
751 } else {
752 SourceFileScanner sfs = new SourceFileScanner(this);
753 newFiles = sfs.restrict(files, baseDir, getOutputDir(), mapper);
754 }
755 for (int i = 0; i < newFiles.length; i++) {
756 String name = newFiles[i].replace(File.separatorChar, '.');
757 name = name.substring(0, name.lastIndexOf(".class"));
758 compileList.addElement(name);
759 }
760 }
761
762 /**
763 * Load named class and test whether it can be rmic'ed
764 * @param classname the name of the class to be tested
765 * @return true if the class can be rmic'ed
766 */
767 public boolean isValidRmiRemote(String classname) {
768 try {
769 Class testClass = loader.loadClass(classname);
770 // One cannot RMIC an interface for "classic" RMI (JRMP)
771 if (testClass.isInterface() && !iiop && !idl) {
772 return false;
773 }
774 return isValidRmiRemote(testClass);
775 } catch (ClassNotFoundException e) {
776 log(ERROR_UNABLE_TO_VERIFY_CLASS + classname + ERROR_NOT_FOUND,
777 Project.MSG_WARN);
778 } catch (NoClassDefFoundError e) {
779 log(ERROR_UNABLE_TO_VERIFY_CLASS + classname + ERROR_NOT_DEFINED,
780 Project.MSG_WARN);
781 } catch (Throwable t) {
782 log(ERROR_UNABLE_TO_VERIFY_CLASS + classname
783 + ERROR_LOADING_CAUSED_EXCEPTION + t.getMessage(),
784 Project.MSG_WARN);
785 }
786 // we only get here if an exception has been thrown
787 return false;
788 }
789
790 /**
791 * Returns the topmost interface that extends Remote for a given
792 * class - if one exists.
793 * @param testClass the class to be tested
794 * @return the topmost interface that extends Remote, or null if there
795 * is none.
796 */
797 public Class getRemoteInterface(Class testClass) {
798 if (Remote.class.isAssignableFrom(testClass)) {
799 Class [] interfaces = testClass.getInterfaces();
800 if (interfaces != null) {
801 for (int i = 0; i < interfaces.length; i++) {
802 if (Remote.class.isAssignableFrom(interfaces[i])) {
803 return interfaces[i];
804 }
805 }
806 }
807 }
808 return null;
809 }
810
811 /**
812 * Check to see if the class or (super)interfaces implement
813 * java.rmi.Remote.
814 */
815 private boolean isValidRmiRemote (Class testClass) {
816 return getRemoteInterface(testClass) != null;
817 }
818
819 /**
820 * Classloader for the user-specified classpath.
821 * @return the classloader
822 */
823 public ClassLoader getLoader() {
824 return loader;
825 }
826
827 /**
828 * Adds an "compiler" attribute to Commandline$Attribute used to
829 * filter command line attributes based on the current
830 * implementation.
831 */
832 public class ImplementationSpecificArgument extends
833 org.apache.tools.ant.util.facade.ImplementationSpecificArgument {
834 /**
835 * Only pass the specified argument if the
836 * chosen compiler implementation matches the
837 * value of this attribute. Legal values are
838 * the same as those in the above list of
839 * valid compilers.)
840 * @param impl the compiler to be used.
841 */
842 public void setCompiler(String impl) {
843 super.setImplementation(impl);
844 }
845 }
846 }