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
19 package org.apache.tools.ant.taskdefs;
20
21 import java.io.File;
22
23 import java.util.ArrayList;
24 import java.util.Iterator;
25 import java.util.List;
26
27 import org.apache.tools.ant.BuildException;
28 import org.apache.tools.ant.DirectoryScanner;
29 import org.apache.tools.ant.MagicNames;
30 import org.apache.tools.ant.Project;
31 import org.apache.tools.ant.taskdefs.compilers.CompilerAdapter;
32 import org.apache.tools.ant.taskdefs.compilers.CompilerAdapterFactory;
33 import org.apache.tools.ant.types.Path;
34 import org.apache.tools.ant.types.Reference;
35 import org.apache.tools.ant.util.GlobPatternMapper;
36 import org.apache.tools.ant.util.JavaEnvUtils;
37 import org.apache.tools.ant.util.SourceFileScanner;
38 import org.apache.tools.ant.util.facade.FacadeTaskHelper;
39
40 /**
41 * Compiles Java source files. This task can take the following
42 * arguments:
43 * <ul>
44 * <li>sourcedir
45 * <li>destdir
46 * <li>deprecation
47 * <li>classpath
48 * <li>bootclasspath
49 * <li>extdirs
50 * <li>optimize
51 * <li>debug
52 * <li>encoding
53 * <li>target
54 * <li>depend
55 * <li>verbose
56 * <li>failonerror
57 * <li>includeantruntime
58 * <li>includejavaruntime
59 * <li>source
60 * <li>compiler
61 * </ul>
62 * Of these arguments, the <b>sourcedir</b> and <b>destdir</b> are required.
63 * <p>
64 * When this task executes, it will recursively scan the sourcedir and
65 * destdir looking for Java source files to compile. This task makes its
66 * compile decision based on timestamp.
67 *
68 *
69 * @since Ant 1.1
70 *
71 * @ant.task category="java"
72 */
73
74 public class Javac extends MatchingTask {
75
76 private static final String FAIL_MSG
77 = "Compile failed; see the compiler error output for details.";
78
79 private static final String JAVAC16 = "javac1.6";
80 private static final String JAVAC15 = "javac1.5";
81 private static final String JAVAC14 = "javac1.4";
82 private static final String JAVAC13 = "javac1.3";
83 private static final String JAVAC12 = "javac1.2";
84 private static final String JAVAC11 = "javac1.1";
85 private static final String MODERN = "modern";
86 private static final String CLASSIC = "classic";
87 private static final String EXTJAVAC = "extJavac";
88
89 private static final String PACKAGE_INFO_JAVA = "package-info.java";
90 private static final String PACKAGE_INFO_CLASS = "package-info.class";
91
92 private Path src;
93 private File destDir;
94 private Path compileClasspath;
95 private Path compileSourcepath;
96 private String encoding;
97 private boolean debug = false;
98 private boolean optimize = false;
99 private boolean deprecation = false;
100 private boolean depend = false;
101 private boolean verbose = false;
102 private String targetAttribute;
103 private Path bootclasspath;
104 private Path extdirs;
105 private boolean includeAntRuntime = true;
106 private boolean includeJavaRuntime = false;
107 private boolean fork = false;
108 private String forkedExecutable = null;
109 private boolean nowarn = false;
110 private String memoryInitialSize;
111 private String memoryMaximumSize;
112 private FacadeTaskHelper facade = null;
113
114 // CheckStyle:VisibilityModifier OFF - bc
115 protected boolean failOnError = true;
116 protected boolean listFiles = false;
117 protected File[] compileList = new File[0];
118 // CheckStyle:VisibilityModifier ON
119
120 private String source;
121 private String debugLevel;
122 private File tmpDir;
123 private String updatedProperty;
124 private String errorProperty;
125 private boolean taskSuccess = true; // assume the best
126 private boolean includeDestClasses = true;
127 private List updateDirList = new ArrayList();
128
129 /**
130 * Javac task for compilation of Java files.
131 */
132 public Javac() {
133 facade = new FacadeTaskHelper(assumedJavaVersion());
134 }
135
136 private String assumedJavaVersion() {
137 if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2)) {
138 return JAVAC12;
139 } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3)) {
140 return JAVAC13;
141 } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_4)) {
142 return JAVAC14;
143 } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_5)) {
144 return JAVAC15;
145 } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_6)) {
146 return JAVAC16;
147 } else {
148 return CLASSIC;
149 }
150 }
151
152 /**
153 * Get the value of debugLevel.
154 * @return value of debugLevel.
155 */
156 public String getDebugLevel() {
157 return debugLevel;
158 }
159
160 /**
161 * Keyword list to be appended to the -g command-line switch.
162 *
163 * This will be ignored by all implementations except modern
164 * and classic(ver >= 1.2). Legal values are none or a
165 * comma-separated list of the following keywords: lines, vars,
166 * and source. If debuglevel is not specified, by default, :none
167 * will be appended to -g. If debug is not turned on, this attribute
168 * will be ignored.
169 *
170 * @param v Value to assign to debugLevel.
171 */
172 public void setDebugLevel(String v) {
173 this.debugLevel = v;
174 }
175
176 /**
177 * Get the value of source.
178 * @return value of source.
179 */
180 public String getSource() {
181 return source != null
182 ? source : getProject().getProperty(MagicNames.BUILD_JAVAC_SOURCE);
183 }
184
185 /**
186 * Value of the -source command-line switch; will be ignored
187 * by all implementations except modern and jikes.
188 *
189 * If you use this attribute together with jikes, you must make
190 * sure that your version of jikes supports the -source switch.
191 * Legal values are 1.3, 1.4, 1.5, and 5 - by default, no
192 * -source argument will be used at all.
193 *
194 * @param v Value to assign to source.
195 */
196 public void setSource(String v) {
197 this.source = v;
198 }
199
200 /**
201 * Adds a path for source compilation.
202 *
203 * @return a nested src element.
204 */
205 public Path createSrc() {
206 if (src == null) {
207 src = new Path(getProject());
208 }
209 return src.createPath();
210 }
211
212 /**
213 * Recreate src.
214 *
215 * @return a nested src element.
216 */
217 protected Path recreateSrc() {
218 src = null;
219 return createSrc();
220 }
221
222 /**
223 * Set the source directories to find the source Java files.
224 * @param srcDir the source directories as a path
225 */
226 public void setSrcdir(Path srcDir) {
227 if (src == null) {
228 src = srcDir;
229 } else {
230 src.append(srcDir);
231 }
232 }
233
234 /**
235 * Gets the source dirs to find the source java files.
236 * @return the source directories as a path
237 */
238 public Path getSrcdir() {
239 return src;
240 }
241
242 /**
243 * Set the destination directory into which the Java source
244 * files should be compiled.
245 * @param destDir the destination director
246 */
247 public void setDestdir(File destDir) {
248 this.destDir = destDir;
249 }
250
251 /**
252 * Gets the destination directory into which the java source files
253 * should be compiled.
254 * @return the destination directory
255 */
256 public File getDestdir() {
257 return destDir;
258 }
259
260 /**
261 * Set the sourcepath to be used for this compilation.
262 * @param sourcepath the source path
263 */
264 public void setSourcepath(Path sourcepath) {
265 if (compileSourcepath == null) {
266 compileSourcepath = sourcepath;
267 } else {
268 compileSourcepath.append(sourcepath);
269 }
270 }
271
272 /**
273 * Gets the sourcepath to be used for this compilation.
274 * @return the source path
275 */
276 public Path getSourcepath() {
277 return compileSourcepath;
278 }
279
280 /**
281 * Adds a path to sourcepath.
282 * @return a sourcepath to be configured
283 */
284 public Path createSourcepath() {
285 if (compileSourcepath == null) {
286 compileSourcepath = new Path(getProject());
287 }
288 return compileSourcepath.createPath();
289 }
290
291 /**
292 * Adds a reference to a source path defined elsewhere.
293 * @param r a reference to a source path
294 */
295 public void setSourcepathRef(Reference r) {
296 createSourcepath().setRefid(r);
297 }
298
299 /**
300 * Set the classpath to be used for this compilation.
301 *
302 * @param classpath an Ant Path object containing the compilation classpath.
303 */
304 public void setClasspath(Path classpath) {
305 if (compileClasspath == null) {
306 compileClasspath = classpath;
307 } else {
308 compileClasspath.append(classpath);
309 }
310 }
311
312 /**
313 * Gets the classpath to be used for this compilation.
314 * @return the class path
315 */
316 public Path getClasspath() {
317 return compileClasspath;
318 }
319
320 /**
321 * Adds a path to the classpath.
322 * @return a class path to be configured
323 */
324 public Path createClasspath() {
325 if (compileClasspath == null) {
326 compileClasspath = new Path(getProject());
327 }
328 return compileClasspath.createPath();
329 }
330
331 /**
332 * Adds a reference to a classpath defined elsewhere.
333 * @param r a reference to a classpath
334 */
335 public void setClasspathRef(Reference r) {
336 createClasspath().setRefid(r);
337 }
338
339 /**
340 * Sets the bootclasspath that will be used to compile the classes
341 * against.
342 * @param bootclasspath a path to use as a boot class path (may be more
343 * than one)
344 */
345 public void setBootclasspath(Path bootclasspath) {
346 if (this.bootclasspath == null) {
347 this.bootclasspath = bootclasspath;
348 } else {
349 this.bootclasspath.append(bootclasspath);
350 }
351 }
352
353 /**
354 * Gets the bootclasspath that will be used to compile the classes
355 * against.
356 * @return the boot path
357 */
358 public Path getBootclasspath() {
359 return bootclasspath;
360 }
361
362 /**
363 * Adds a path to the bootclasspath.
364 * @return a path to be configured
365 */
366 public Path createBootclasspath() {
367 if (bootclasspath == null) {
368 bootclasspath = new Path(getProject());
369 }
370 return bootclasspath.createPath();
371 }
372
373 /**
374 * Adds a reference to a classpath defined elsewhere.
375 * @param r a reference to a classpath
376 */
377 public void setBootClasspathRef(Reference r) {
378 createBootclasspath().setRefid(r);
379 }
380
381 /**
382 * Sets the extension directories that will be used during the
383 * compilation.
384 * @param extdirs a path
385 */
386 public void setExtdirs(Path extdirs) {
387 if (this.extdirs == null) {
388 this.extdirs = extdirs;
389 } else {
390 this.extdirs.append(extdirs);
391 }
392 }
393
394 /**
395 * Gets the extension directories that will be used during the
396 * compilation.
397 * @return the extension directories as a path
398 */
399 public Path getExtdirs() {
400 return extdirs;
401 }
402
403 /**
404 * Adds a path to extdirs.
405 * @return a path to be configured
406 */
407 public Path createExtdirs() {
408 if (extdirs == null) {
409 extdirs = new Path(getProject());
410 }
411 return extdirs.createPath();
412 }
413
414 /**
415 * If true, list the source files being handed off to the compiler.
416 * @param list if true list the source files
417 */
418 public void setListfiles(boolean list) {
419 listFiles = list;
420 }
421
422 /**
423 * Get the listfiles flag.
424 * @return the listfiles flag
425 */
426 public boolean getListfiles() {
427 return listFiles;
428 }
429
430 /**
431 * Indicates whether the build will continue
432 * even if there are compilation errors; defaults to true.
433 * @param fail if true halt the build on failure
434 */
435 public void setFailonerror(boolean fail) {
436 failOnError = fail;
437 }
438
439 /**
440 * @ant.attribute ignore="true"
441 * @param proceed inverse of failoferror
442 */
443 public void setProceed(boolean proceed) {
444 failOnError = !proceed;
445 }
446
447 /**
448 * Gets the failonerror flag.
449 * @return the failonerror flag
450 */
451 public boolean getFailonerror() {
452 return failOnError;
453 }
454
455 /**
456 * Indicates whether source should be
457 * compiled with deprecation information; defaults to off.
458 * @param deprecation if true turn on deprecation information
459 */
460 public void setDeprecation(boolean deprecation) {
461 this.deprecation = deprecation;
462 }
463
464 /**
465 * Gets the deprecation flag.
466 * @return the deprecation flag
467 */
468 public boolean getDeprecation() {
469 return deprecation;
470 }
471
472 /**
473 * The initial size of the memory for the underlying VM
474 * if javac is run externally; ignored otherwise.
475 * Defaults to the standard VM memory setting.
476 * (Examples: 83886080, 81920k, or 80m)
477 * @param memoryInitialSize string to pass to VM
478 */
479 public void setMemoryInitialSize(String memoryInitialSize) {
480 this.memoryInitialSize = memoryInitialSize;
481 }
482
483 /**
484 * Gets the memoryInitialSize flag.
485 * @return the memoryInitialSize flag
486 */
487 public String getMemoryInitialSize() {
488 return memoryInitialSize;
489 }
490
491 /**
492 * The maximum size of the memory for the underlying VM
493 * if javac is run externally; ignored otherwise.
494 * Defaults to the standard VM memory setting.
495 * (Examples: 83886080, 81920k, or 80m)
496 * @param memoryMaximumSize string to pass to VM
497 */
498 public void setMemoryMaximumSize(String memoryMaximumSize) {
499 this.memoryMaximumSize = memoryMaximumSize;
500 }
501
502 /**
503 * Gets the memoryMaximumSize flag.
504 * @return the memoryMaximumSize flag
505 */
506 public String getMemoryMaximumSize() {
507 return memoryMaximumSize;
508 }
509
510 /**
511 * Set the Java source file encoding name.
512 * @param encoding the source file encoding
513 */
514 public void setEncoding(String encoding) {
515 this.encoding = encoding;
516 }
517
518 /**
519 * Gets the java source file encoding name.
520 * @return the source file encoding name
521 */
522 public String getEncoding() {
523 return encoding;
524 }
525
526 /**
527 * Indicates whether source should be compiled
528 * with debug information; defaults to off.
529 * @param debug if true compile with debug information
530 */
531 public void setDebug(boolean debug) {
532 this.debug = debug;
533 }
534
535 /**
536 * Gets the debug flag.
537 * @return the debug flag
538 */
539 public boolean getDebug() {
540 return debug;
541 }
542
543 /**
544 * If true, compiles with optimization enabled.
545 * @param optimize if true compile with optimization enabled
546 */
547 public void setOptimize(boolean optimize) {
548 this.optimize = optimize;
549 }
550
551 /**
552 * Gets the optimize flag.
553 * @return the optimize flag
554 */
555 public boolean getOptimize() {
556 return optimize;
557 }
558
559 /**
560 * Enables dependency-tracking for compilers
561 * that support this (jikes and classic).
562 * @param depend if true enable dependency-tracking
563 */
564 public void setDepend(boolean depend) {
565 this.depend = depend;
566 }
567
568 /**
569 * Gets the depend flag.
570 * @return the depend flag
571 */
572 public boolean getDepend() {
573 return depend;
574 }
575
576 /**
577 * If true, asks the compiler for verbose output.
578 * @param verbose if true, asks the compiler for verbose output
579 */
580 public void setVerbose(boolean verbose) {
581 this.verbose = verbose;
582 }
583
584 /**
585 * Gets the verbose flag.
586 * @return the verbose flag
587 */
588 public boolean getVerbose() {
589 return verbose;
590 }
591
592 /**
593 * Sets the target VM that the classes will be compiled for. Valid
594 * values depend on the compiler, for jdk 1.4 the valid values are
595 * "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "5" and "6".
596 * @param target the target VM
597 */
598 public void setTarget(String target) {
599 this.targetAttribute = target;
600 }
601
602 /**
603 * Gets the target VM that the classes will be compiled for.
604 * @return the target VM
605 */
606 public String getTarget() {
607 return targetAttribute != null
608 ? targetAttribute
609 : getProject().getProperty(MagicNames.BUILD_JAVAC_TARGET);
610 }
611
612 /**
613 * If true, includes Ant's own classpath in the classpath.
614 * @param include if true, includes Ant's own classpath in the classpath
615 */
616 public void setIncludeantruntime(boolean include) {
617 includeAntRuntime = include;
618 }
619
620 /**
621 * Gets whether or not the ant classpath is to be included in the classpath.
622 * @return whether or not the ant classpath is to be included in the classpath
623 */
624 public boolean getIncludeantruntime() {
625 return includeAntRuntime;
626 }
627
628 /**
629 * If true, includes the Java runtime libraries in the classpath.
630 * @param include if true, includes the Java runtime libraries in the classpath
631 */
632 public void setIncludejavaruntime(boolean include) {
633 includeJavaRuntime = include;
634 }
635
636 /**
637 * Gets whether or not the java runtime should be included in this
638 * task's classpath.
639 * @return the includejavaruntime attribute
640 */
641 public boolean getIncludejavaruntime() {
642 return includeJavaRuntime;
643 }
644
645 /**
646 * If true, forks the javac compiler.
647 *
648 * @param f "true|false|on|off|yes|no"
649 */
650 public void setFork(boolean f) {
651 fork = f;
652 }
653
654 /**
655 * Sets the name of the javac executable.
656 *
657 * <p>Ignored unless fork is true or extJavac has been specified
658 * as the compiler.</p>
659 * @param forkExec the name of the executable
660 */
661 public void setExecutable(String forkExec) {
662 forkedExecutable = forkExec;
663 }
664
665 /**
666 * The value of the executable attribute, if any.
667 *
668 * @since Ant 1.6
669 * @return the name of the java executable
670 */
671 public String getExecutable() {
672 return forkedExecutable;
673 }
674
675 /**
676 * Is this a forked invocation of JDK's javac?
677 * @return true if this is a forked invocation
678 */
679 public boolean isForkedJavac() {
680 return fork || "extJavac".equals(getCompiler());
681 }
682
683 /**
684 * The name of the javac executable to use in fork-mode.
685 *
686 * <p>This is either the name specified with the executable
687 * attribute or the full path of the javac compiler of the VM Ant
688 * is currently running in - guessed by Ant.</p>
689 *
690 * <p>You should <strong>not</strong> invoke this method if you
691 * want to get the value of the executable command - use {@link
692 * #getExecutable getExecutable} for this.</p>
693 * @return the name of the javac executable
694 */
695 public String getJavacExecutable() {
696 if (forkedExecutable == null && isForkedJavac()) {
697 forkedExecutable = getSystemJavac();
698 } else if (forkedExecutable != null && !isForkedJavac()) {
699 forkedExecutable = null;
700 }
701 return forkedExecutable;
702 }
703
704 /**
705 * If true, enables the -nowarn option.
706 * @param flag if true, enable the -nowarn option
707 */
708 public void setNowarn(boolean flag) {
709 this.nowarn = flag;
710 }
711
712 /**
713 * Should the -nowarn option be used.
714 * @return true if the -nowarn option should be used
715 */
716 public boolean getNowarn() {
717 return nowarn;
718 }
719
720 /**
721 * Adds an implementation specific command-line argument.
722 * @return a ImplementationSpecificArgument to be configured
723 */
724 public ImplementationSpecificArgument createCompilerArg() {
725 ImplementationSpecificArgument arg =
726 new ImplementationSpecificArgument();
727 facade.addImplementationArgument(arg);
728 return arg;
729 }
730
731 /**
732 * Get the additional implementation specific command line arguments.
733 * @return array of command line arguments, guaranteed to be non-null.
734 */
735 public String[] getCurrentCompilerArgs() {
736 String chosen = facade.getExplicitChoice();
737 try {
738 // make sure facade knows about magic properties and fork setting
739 String appliedCompiler = getCompiler();
740 facade.setImplementation(appliedCompiler);
741
742 String[] result = facade.getArgs();
743
744 String altCompilerName = getAltCompilerName(facade.getImplementation());
745
746 if (result.length == 0 && altCompilerName != null) {
747 facade.setImplementation(altCompilerName);
748 result = facade.getArgs();
749 }
750
751 return result;
752
753 } finally {
754 facade.setImplementation(chosen);
755 }
756 }
757
758 private String getAltCompilerName(String anImplementation) {
759 if (JAVAC16.equalsIgnoreCase(anImplementation)
760 || JAVAC15.equalsIgnoreCase(anImplementation)
761 || JAVAC14.equalsIgnoreCase(anImplementation)
762 || JAVAC13.equalsIgnoreCase(anImplementation)) {
763 return MODERN;
764 }
765 if (JAVAC12.equalsIgnoreCase(anImplementation)
766 || JAVAC11.equalsIgnoreCase(anImplementation)) {
767 return CLASSIC;
768 }
769 if (MODERN.equalsIgnoreCase(anImplementation)) {
770 String nextSelected = assumedJavaVersion();
771 if (JAVAC16.equalsIgnoreCase(nextSelected)
772 || JAVAC15.equalsIgnoreCase(nextSelected)
773 || JAVAC14.equalsIgnoreCase(nextSelected)
774 || JAVAC13.equalsIgnoreCase(nextSelected)) {
775 return nextSelected;
776 }
777 }
778 if (CLASSIC.equals(anImplementation)) {
779 return assumedJavaVersion();
780 }
781 if (EXTJAVAC.equalsIgnoreCase(anImplementation)) {
782 return assumedJavaVersion();
783 }
784 return null;
785 }
786
787 /**
788 * Where Ant should place temporary files.
789 *
790 * @since Ant 1.6
791 * @param tmpDir the temporary directory
792 */
793 public void setTempdir(File tmpDir) {
794 this.tmpDir = tmpDir;
795 }
796
797 /**
798 * Where Ant should place temporary files.
799 *
800 * @since Ant 1.6
801 * @return the temporary directory
802 */
803 public File getTempdir() {
804 return tmpDir;
805 }
806
807 /**
808 * The property to set on compliation success.
809 * This property will not be set if the compilation
810 * fails, or if there are no files to compile.
811 * @param updatedProperty the property name to use.
812 * @since Ant 1.7.1.
813 */
814 public void setUpdatedProperty(String updatedProperty) {
815 this.updatedProperty = updatedProperty;
816 }
817
818 /**
819 * The property to set on compliation failure.
820 * This property will be set if the compilation
821 * fails.
822 * @param errorProperty the property name to use.
823 * @since Ant 1.7.1.
824 */
825 public void setErrorProperty(String errorProperty) {
826 this.errorProperty = errorProperty;
827 }
828
829 /**
830 * This property controls whether to include the
831 * destination classes directory in the classpath
832 * given to the compiler.
833 * The default value is "true".
834 * @param includeDestClasses the value to use.
835 */
836 public void setIncludeDestClasses(boolean includeDestClasses) {
837 this.includeDestClasses = includeDestClasses;
838 }
839
840 /**
841 * Get the value of the includeDestClasses property.
842 * @return the value.
843 */
844 public boolean isIncludeDestClasses() {
845 return includeDestClasses;
846 }
847
848 /**
849 * Get the result of the javac task (success or failure).
850 * @return true if compilation succeeded, or
851 * was not neccessary, false if the compilation failed.
852 */
853 public boolean getTaskSuccess() {
854 return taskSuccess;
855 }
856
857 /**
858 * Executes the task.
859 * @exception BuildException if an error occurs
860 */
861 public void execute() throws BuildException {
862 checkParameters();
863 resetFileLists();
864
865 // scan source directories and dest directory to build up
866 // compile lists
867 String[] list = src.list();
868 for (int i = 0; i < list.length; i++) {
869 File srcDir = getProject().resolveFile(list[i]);
870 if (!srcDir.exists()) {
871 throw new BuildException("srcdir \""
872 + srcDir.getPath()
873 + "\" does not exist!", getLocation());
874 }
875
876 DirectoryScanner ds = this.getDirectoryScanner(srcDir);
877 String[] files = ds.getIncludedFiles();
878
879 scanDir(srcDir, destDir != null ? destDir : srcDir, files);
880 }
881
882 compile();
883 if (updatedProperty != null
884 && taskSuccess
885 && compileList.length != 0) {
886 getProject().setNewProperty(updatedProperty, "true");
887 }
888 }
889
890 /**
891 * Clear the list of files to be compiled and copied..
892 */
893 protected void resetFileLists() {
894 compileList = new File[0];
895 }
896
897 /**
898 * Scans the directory looking for source files to be compiled.
899 * The results are returned in the class variable compileList
900 *
901 * @param srcDir The source directory
902 * @param destDir The destination directory
903 * @param files An array of filenames
904 */
905 protected void scanDir(File srcDir, File destDir, String[] files) {
906 GlobPatternMapper m = new GlobPatternMapper();
907 m.setFrom("*.java");
908 m.setTo("*.class");
909 SourceFileScanner sfs = new SourceFileScanner(this);
910 File[] newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m);
911
912 newFiles = removePackageInfoFiles(newFiles, srcDir, destDir);
913 if (newFiles.length > 0) {
914 File[] newCompileList
915 = new File[compileList.length + newFiles.length];
916 System.arraycopy(compileList, 0, newCompileList, 0,
917 compileList.length);
918 System.arraycopy(newFiles, 0, newCompileList,
919 compileList.length, newFiles.length);
920 compileList = newCompileList;
921 }
922 }
923
924 /**
925 * Gets the list of files to be compiled.
926 * @return the list of files as an array
927 */
928 public File[] getFileList() {
929 return compileList;
930 }
931
932 /**
933 * Is the compiler implementation a jdk compiler
934 *
935 * @param compilerImpl the name of the compiler implementation
936 * @return true if compilerImpl is "modern", "classic",
937 * "javac1.1", "javac1.2", "javac1.3", "javac1.4", "javac1.5" or
938 * "javac1.6".
939 */
940 protected boolean isJdkCompiler(String compilerImpl) {
941 return MODERN.equals(compilerImpl)
942 || CLASSIC.equals(compilerImpl)
943 || JAVAC16.equals(compilerImpl)
944 || JAVAC15.equals(compilerImpl)
945 || JAVAC14.equals(compilerImpl)
946 || JAVAC13.equals(compilerImpl)
947 || JAVAC12.equals(compilerImpl)
948 || JAVAC11.equals(compilerImpl);
949 }
950
951 /**
952 * @return the executable name of the java compiler
953 */
954 protected String getSystemJavac() {
955 return JavaEnvUtils.getJdkExecutable("javac");
956 }
957
958 /**
959 * Choose the implementation for this particular task.
960 * @param compiler the name of the compiler
961 * @since Ant 1.5
962 */
963 public void setCompiler(String compiler) {
964 facade.setImplementation(compiler);
965 }
966
967 /**
968 * The implementation for this particular task.
969 *
970 * <p>Defaults to the build.compiler property but can be overridden
971 * via the compiler and fork attributes.</p>
972 *
973 * <p>If fork has been set to true, the result will be extJavac
974 * and not classic or java1.2 - no matter what the compiler
975 * attribute looks like.</p>
976 *
977 * @see #getCompilerVersion
978 * @return the compiler.
979 * @since Ant 1.5
980 */
981 public String getCompiler() {
982 String compilerImpl = getCompilerVersion();
983 if (fork) {
984 if (isJdkCompiler(compilerImpl)) {
985 compilerImpl = "extJavac";
986 } else {
987 log("Since compiler setting isn't classic or modern,"
988 + "ignoring fork setting.", Project.MSG_WARN);
989 }
990 }
991 return compilerImpl;
992 }
993
994 /**
995 * The implementation for this particular task.
996 *
997 * <p>Defaults to the build.compiler property but can be overridden
998 * via the compiler attribute.</p>
999 *
1000 * <p>This method does not take the fork attribute into
1001 * account.</p>
1002 *
1003 * @see #getCompiler
1004 * @return the compiler.
1005 *
1006 * @since Ant 1.5
1007 */
1008 public String getCompilerVersion() {
1009 facade.setMagicValue(getProject().getProperty("build.compiler"));
1010 return facade.getImplementation();
1011 }
1012
1013 /**
1014 * Check that all required attributes have been set and nothing
1015 * silly has been entered.
1016 *
1017 * @since Ant 1.5
1018 * @exception BuildException if an error occurs
1019 */
1020 protected void checkParameters() throws BuildException {
1021 if (src == null) {
1022 throw new BuildException("srcdir attribute must be set!",
1023 getLocation());
1024 }
1025 if (src.size() == 0) {
1026 throw new BuildException("srcdir attribute must be set!",
1027 getLocation());
1028 }
1029
1030 if (destDir != null && !destDir.isDirectory()) {
1031 throw new BuildException("destination directory \""
1032 + destDir
1033 + "\" does not exist "
1034 + "or is not a directory", getLocation());
1035 }
1036 }
1037
1038 /**
1039 * Perform the compilation.
1040 *
1041 * @since Ant 1.5
1042 */
1043 protected void compile() {
1044 String compilerImpl = getCompiler();
1045
1046 if (compileList.length > 0) {
1047 log("Compiling " + compileList.length + " source file"
1048 + (compileList.length == 1 ? "" : "s")
1049 + (destDir != null ? " to " + destDir : ""));
1050
1051 if (listFiles) {
1052 for (int i = 0; i < compileList.length; i++) {
1053 String filename = compileList[i].getAbsolutePath();
1054 log(filename);
1055 }
1056 }
1057
1058 CompilerAdapter adapter =
1059 CompilerAdapterFactory.getCompiler(compilerImpl, this);
1060
1061 // now we need to populate the compiler adapter
1062 adapter.setJavac(this);
1063
1064 // finally, lets execute the compiler!!
1065 if (adapter.execute()) {
1066 // Success - check
1067 for (Iterator i = updateDirList.iterator(); i.hasNext();) {
1068 File file = (File) i.next();
1069 file.setLastModified(System.currentTimeMillis());
1070 }
1071 } else {
1072 // Fail path
1073 this.taskSuccess = false;
1074 if (errorProperty != null) {
1075 getProject().setNewProperty(
1076 errorProperty, "true");
1077 }
1078 if (failOnError) {
1079 throw new BuildException(FAIL_MSG, getLocation());
1080 } else {
1081 log(FAIL_MSG, Project.MSG_ERR);
1082 }
1083 }
1084 }
1085 }
1086
1087 /**
1088 * Adds an "compiler" attribute to Commandline$Attribute used to
1089 * filter command line attributes based on the current
1090 * implementation.
1091 */
1092 public class ImplementationSpecificArgument extends
1093 org.apache.tools.ant.util.facade.ImplementationSpecificArgument {
1094
1095 /**
1096 * @param impl the name of the compiler
1097 */
1098 public void setCompiler(String impl) {
1099 super.setImplementation(impl);
1100 }
1101 }
1102
1103 // ----------------------------------------------------------------
1104 // Code to remove package-info.java files from compilation
1105 // Since Ant 1.7.1.
1106 //
1107 // package-info.java are files that contain package level
1108 // annotations. They may or may not have corresponding .class
1109 // files.
1110 //
1111 // The following code uses the algorithm:
1112 // * on entry we have the files that need to be compiled
1113 // * if the filename is not package-info.java compile it
1114 // * if a corresponding .class file exists compile it
1115 // * if the corresponding class directory does not exist compile it
1116 // * if the corresponding directory lastmodifed time is
1117 // older than the java file, compile the java file and
1118 // touch the corresponding class directory (on successful
1119 // compilation).
1120 //
1121 // ----------------------------------------------------------------
1122 private File[] removePackageInfoFiles(
1123 File[] newFiles, File srcDir, File destDir) {
1124 if (!hasPackageInfo(newFiles)) {
1125 return newFiles;
1126 }
1127 List ret = new ArrayList();
1128 for (int i = 0; i < newFiles.length; ++i) {
1129 if (needsCompilePackageFile(newFiles[i], srcDir, destDir)) {
1130 ret.add(newFiles[i]);
1131 }
1132 }
1133 return (File[]) ret.toArray(new File[0]);
1134 }
1135
1136 private boolean hasPackageInfo(File[] newFiles) {
1137 for (int i = 0; i < newFiles.length; ++i) {
1138 if (newFiles[i].getName().equals(PACKAGE_INFO_JAVA)) {
1139 return true;
1140 }
1141 }
1142 return false;
1143 }
1144
1145 private boolean needsCompilePackageFile(
1146 File file, File srcDir, File destDir) {
1147 if (!file.getName().equals(PACKAGE_INFO_JAVA)) {
1148 return true;
1149 }
1150 // return true if destDir contains the file
1151 String rel = relativePath(srcDir, file);
1152 File destFile = new File(destDir, rel);
1153 File parent = destFile.getParentFile();
1154 destFile = new File(parent, PACKAGE_INFO_CLASS);
1155 File sourceFile = new File(srcDir, rel);
1156 if (destFile.exists()) {
1157 return true;
1158 }
1159 // Dest file does not exist
1160 // Compile Source file if sourceFile is newer that destDir
1161 // TODO - use fs
1162 if (sourceFile.lastModified()
1163 > destFile.getParentFile().lastModified()) {
1164 updateDirList.add(destFile.getParentFile());
1165 return true;
1166 }
1167 return false;
1168 }
1169
1170 private String relativePath(File src, File file) {
1171 return file.getAbsolutePath().substring(
1172 src.getAbsolutePath().length() + 1);
1173 }
1174 }