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.compilers;
20
21 //Java5 style
22 //import static org.apache.tools.ant.util.StringUtils.LINE_SEP;
23
24 import java.io.File;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.io.PrintWriter;
28 import org.apache.tools.ant.BuildException;
29 import org.apache.tools.ant.Location;
30 import org.apache.tools.ant.Project;
31 import org.apache.tools.ant.taskdefs.Execute;
32 import org.apache.tools.ant.taskdefs.Javac;
33 import org.apache.tools.ant.taskdefs.LogStreamHandler;
34 import org.apache.tools.ant.types.Commandline;
35 import org.apache.tools.ant.types.Path;
36 import org.apache.tools.ant.util.FileUtils;
37 import org.apache.tools.ant.util.StringUtils;
38 import org.apache.tools.ant.util.JavaEnvUtils;
39 import org.apache.tools.ant.taskdefs.condition.Os;
40
41 /**
42 * This is the default implementation for the CompilerAdapter interface.
43 * Currently, this is a cut-and-paste of the original javac task.
44 *
45 * @since Ant 1.3
46 */
47 public abstract class DefaultCompilerAdapter implements CompilerAdapter {
48 private static final int COMMAND_LINE_LIMIT = 4096; // 4K
49 // CheckStyle:VisibilityModifier OFF - bc
50
51 private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
52
53 protected Path src;
54 protected File destDir;
55 protected String encoding;
56 protected boolean debug = false;
57 protected boolean optimize = false;
58 protected boolean deprecation = false;
59 protected boolean depend = false;
60 protected boolean verbose = false;
61 protected String target;
62 protected Path bootclasspath;
63 protected Path extdirs;
64 protected Path compileClasspath;
65 protected Path compileSourcepath;
66 protected Project project;
67 protected Location location;
68 protected boolean includeAntRuntime;
69 protected boolean includeJavaRuntime;
70 protected String memoryInitialSize;
71 protected String memoryMaximumSize;
72
73 protected File[] compileList;
74 protected Javac attributes;
75
76 //must keep for subclass BC, though unused:
77 // CheckStyle:ConstantNameCheck OFF - bc
78 protected static final String lSep = StringUtils.LINE_SEP;
79
80 // CheckStyle:ConstantNameCheck ON
81 // CheckStyle:VisibilityModifier ON
82
83 /**
84 * Set the Javac instance which contains the configured compilation
85 * attributes.
86 *
87 * @param attributes a configured Javac task.
88 */
89 public void setJavac(Javac attributes) {
90 this.attributes = attributes;
91 src = attributes.getSrcdir();
92 destDir = attributes.getDestdir();
93 encoding = attributes.getEncoding();
94 debug = attributes.getDebug();
95 optimize = attributes.getOptimize();
96 deprecation = attributes.getDeprecation();
97 depend = attributes.getDepend();
98 verbose = attributes.getVerbose();
99 target = attributes.getTarget();
100 bootclasspath = attributes.getBootclasspath();
101 extdirs = attributes.getExtdirs();
102 compileList = attributes.getFileList();
103 compileClasspath = attributes.getClasspath();
104 compileSourcepath = attributes.getSourcepath();
105 project = attributes.getProject();
106 location = attributes.getLocation();
107 includeAntRuntime = attributes.getIncludeantruntime();
108 includeJavaRuntime = attributes.getIncludejavaruntime();
109 memoryInitialSize = attributes.getMemoryInitialSize();
110 memoryMaximumSize = attributes.getMemoryMaximumSize();
111 }
112
113 /**
114 * Get the Javac task instance associated with this compiler adapter
115 *
116 * @return the configured Javac task instance used by this adapter.
117 */
118 public Javac getJavac() {
119 return attributes;
120 }
121
122 /**
123 * Get the project this compiler adapter was created in.
124 * @return the owner project
125 * @since Ant 1.6
126 */
127 protected Project getProject() {
128 return project;
129 }
130
131 /**
132 * Builds the compilation classpath.
133 * @return the compilation class path
134 */
135 protected Path getCompileClasspath() {
136 Path classpath = new Path(project);
137
138 // add dest dir to classpath so that previously compiled and
139 // untouched classes are on classpath
140
141 if (destDir != null && getJavac().isIncludeDestClasses()) {
142 classpath.setLocation(destDir);
143 }
144
145 // Combine the build classpath with the system classpath, in an
146 // order determined by the value of build.sysclasspath
147
148 Path cp = compileClasspath;
149 if (cp == null) {
150 cp = new Path(project);
151 }
152 if (includeAntRuntime) {
153 classpath.addExisting(cp.concatSystemClasspath("last"));
154 } else {
155 classpath.addExisting(cp.concatSystemClasspath("ignore"));
156 }
157
158 if (includeJavaRuntime) {
159 classpath.addJavaRuntime();
160 }
161
162 return classpath;
163 }
164
165 /**
166 * Get the command line arguments for the switches.
167 * @param cmd the command line
168 * @return the command line
169 */
170 protected Commandline setupJavacCommandlineSwitches(Commandline cmd) {
171 return setupJavacCommandlineSwitches(cmd, false);
172 }
173
174 /**
175 * Does the command line argument processing common to classic and
176 * modern. Doesn't add the files to compile.
177 * @param cmd the command line
178 * @param useDebugLevel if true set set the debug level with the -g switch
179 * @return the command line
180 */
181 protected Commandline setupJavacCommandlineSwitches(Commandline cmd,
182 boolean useDebugLevel) {
183 Path classpath = getCompileClasspath();
184 // For -sourcepath, use the "sourcepath" value if present.
185 // Otherwise default to the "srcdir" value.
186 Path sourcepath = null;
187 if (compileSourcepath != null) {
188 sourcepath = compileSourcepath;
189 } else {
190 sourcepath = src;
191 }
192
193 String memoryParameterPrefix = assumeJava11() ? "-J-" : "-J-X";
194 if (memoryInitialSize != null) {
195 if (!attributes.isForkedJavac()) {
196 attributes.log("Since fork is false, ignoring "
197 + "memoryInitialSize setting.",
198 Project.MSG_WARN);
199 } else {
200 cmd.createArgument().setValue(memoryParameterPrefix
201 + "ms" + memoryInitialSize);
202 }
203 }
204
205 if (memoryMaximumSize != null) {
206 if (!attributes.isForkedJavac()) {
207 attributes.log("Since fork is false, ignoring "
208 + "memoryMaximumSize setting.",
209 Project.MSG_WARN);
210 } else {
211 cmd.createArgument().setValue(memoryParameterPrefix
212 + "mx" + memoryMaximumSize);
213 }
214 }
215
216 if (attributes.getNowarn()) {
217 cmd.createArgument().setValue("-nowarn");
218 }
219
220 if (deprecation) {
221 cmd.createArgument().setValue("-deprecation");
222 }
223
224 if (destDir != null) {
225 cmd.createArgument().setValue("-d");
226 cmd.createArgument().setFile(destDir);
227 }
228
229 cmd.createArgument().setValue("-classpath");
230
231 // Just add "sourcepath" to classpath ( for JDK1.1 )
232 // as well as "bootclasspath" and "extdirs"
233 if (assumeJava11()) {
234 Path cp = new Path(project);
235
236 Path bp = getBootClassPath();
237 if (bp.size() > 0) {
238 cp.append(bp);
239 }
240
241 if (extdirs != null) {
242 cp.addExtdirs(extdirs);
243 }
244 cp.append(classpath);
245 cp.append(sourcepath);
246 cmd.createArgument().setPath(cp);
247 } else {
248 cmd.createArgument().setPath(classpath);
249 // If the buildfile specifies sourcepath="", then don't
250 // output any sourcepath.
251 if (sourcepath.size() > 0) {
252 cmd.createArgument().setValue("-sourcepath");
253 cmd.createArgument().setPath(sourcepath);
254 }
255 if (target != null) {
256 cmd.createArgument().setValue("-target");
257 cmd.createArgument().setValue(target);
258 }
259
260 Path bp = getBootClassPath();
261 if (bp.size() > 0) {
262 cmd.createArgument().setValue("-bootclasspath");
263 cmd.createArgument().setPath(bp);
264 }
265
266 if (extdirs != null && extdirs.size() > 0) {
267 cmd.createArgument().setValue("-extdirs");
268 cmd.createArgument().setPath(extdirs);
269 }
270 }
271
272 if (encoding != null) {
273 cmd.createArgument().setValue("-encoding");
274 cmd.createArgument().setValue(encoding);
275 }
276 if (debug) {
277 if (useDebugLevel && !assumeJava11()) {
278 String debugLevel = attributes.getDebugLevel();
279 if (debugLevel != null) {
280 cmd.createArgument().setValue("-g:" + debugLevel);
281 } else {
282 cmd.createArgument().setValue("-g");
283 }
284 } else {
285 cmd.createArgument().setValue("-g");
286 }
287 } else if (getNoDebugArgument() != null) {
288 cmd.createArgument().setValue(getNoDebugArgument());
289 }
290 if (optimize) {
291 cmd.createArgument().setValue("-O");
292 }
293
294 if (depend) {
295 if (assumeJava11()) {
296 cmd.createArgument().setValue("-depend");
297 } else if (assumeJava12()) {
298 cmd.createArgument().setValue("-Xdepend");
299 } else {
300 attributes.log("depend attribute is not supported by the "
301 + "modern compiler", Project.MSG_WARN);
302 }
303 }
304
305 if (verbose) {
306 cmd.createArgument().setValue("-verbose");
307 }
308
309 addCurrentCompilerArgs(cmd);
310
311 return cmd;
312 }
313
314 /**
315 * Does the command line argument processing for modern. Doesn't
316 * add the files to compile.
317 * @param cmd the command line
318 * @return the command line
319 */
320 protected Commandline setupModernJavacCommandlineSwitches(Commandline cmd) {
321 setupJavacCommandlineSwitches(cmd, true);
322 if (attributes.getSource() != null && !assumeJava13()) {
323 cmd.createArgument().setValue("-source");
324 String source = attributes.getSource();
325 if (source.equals("1.1") || source.equals("1.2")) {
326 // support for -source 1.1 and -source 1.2 has been
327 // added with JDK 1.4.2 - and isn't present in 1.5.0
328 // or 1.6.0 either
329 cmd.createArgument().setValue("1.3");
330 } else {
331 cmd.createArgument().setValue(source);
332 }
333 } else if ((assumeJava15() || assumeJava16())
334 && attributes.getTarget() != null) {
335 String t = attributes.getTarget();
336 if (t.equals("1.1") || t.equals("1.2") || t.equals("1.3")
337 || t.equals("1.4")) {
338 String s = t;
339 if (t.equals("1.1")) {
340 // 1.5.0 doesn't support -source 1.1
341 s = "1.2";
342 }
343 attributes.log("", Project.MSG_WARN);
344 attributes.log(" WARNING", Project.MSG_WARN);
345 attributes.log("", Project.MSG_WARN);
346 attributes.log("The -source switch defaults to 1.5 in JDK 1.5 and 1.6.",
347 Project.MSG_WARN);
348 attributes.log("If you specify -target " + t
349 + " you now must also specify -source " + s
350 + ".", Project.MSG_WARN);
351 attributes.log("Ant will implicitly add -source " + s
352 + " for you. Please change your build file.",
353 Project.MSG_WARN);
354 cmd.createArgument().setValue("-source");
355 cmd.createArgument().setValue(s);
356 }
357 }
358 return cmd;
359 }
360
361 /**
362 * Does the command line argument processing for modern and adds
363 * the files to compile as well.
364 * @return the command line
365 */
366 protected Commandline setupModernJavacCommand() {
367 Commandline cmd = new Commandline();
368 setupModernJavacCommandlineSwitches(cmd);
369
370 logAndAddFilesToCompile(cmd);
371 return cmd;
372 }
373
374 /**
375 * Set up the command line.
376 * @return the command line
377 */
378 protected Commandline setupJavacCommand() {
379 return setupJavacCommand(false);
380 }
381
382 /**
383 * Does the command line argument processing for classic and adds
384 * the files to compile as well.
385 * @param debugLevelCheck if true set the debug level with the -g switch
386 * @return the command line
387 */
388 protected Commandline setupJavacCommand(boolean debugLevelCheck) {
389 Commandline cmd = new Commandline();
390 setupJavacCommandlineSwitches(cmd, debugLevelCheck);
391 logAndAddFilesToCompile(cmd);
392 return cmd;
393 }
394
395 /**
396 * Logs the compilation parameters, adds the files to compile and logs the
397 * "niceSourceList"
398 * @param cmd the command line
399 */
400 protected void logAndAddFilesToCompile(Commandline cmd) {
401 attributes.log("Compilation " + cmd.describeArguments(),
402 Project.MSG_VERBOSE);
403
404 StringBuffer niceSourceList = new StringBuffer("File");
405 if (compileList.length != 1) {
406 niceSourceList.append("s");
407 }
408 niceSourceList.append(" to be compiled:");
409
410 niceSourceList.append(StringUtils.LINE_SEP);
411
412 for (int i = 0; i < compileList.length; i++) {
413 String arg = compileList[i].getAbsolutePath();
414 cmd.createArgument().setValue(arg);
415 niceSourceList.append(" ");
416 niceSourceList.append(arg);
417 niceSourceList.append(StringUtils.LINE_SEP);
418 }
419
420 attributes.log(niceSourceList.toString(), Project.MSG_VERBOSE);
421 }
422
423 /**
424 * Do the compile with the specified arguments.
425 * @param args - arguments to pass to process on command line
426 * @param firstFileName - index of the first source file in args,
427 * if the index is negative, no temporary file will ever be
428 * created, but this may hit the command line length limit on your
429 * system.
430 * @return the exit code of the compilation
431 */
432 protected int executeExternalCompile(String[] args, int firstFileName) {
433 return executeExternalCompile(args, firstFileName, true);
434 }
435
436 /**
437 * Do the compile with the specified arguments.
438 * @param args - arguments to pass to process on command line
439 * @param firstFileName - index of the first source file in args,
440 * if the index is negative, no temporary file will ever be
441 * created, but this may hit the command line length limit on your
442 * system.
443 * @param quoteFiles - if set to true, filenames containing
444 * spaces will be quoted when they appear in the external file.
445 * This is necessary when running JDK 1.4's javac and probably
446 * others.
447 * @return the exit code of the compilation
448 *
449 * @since Ant 1.6
450 */
451 protected int executeExternalCompile(String[] args, int firstFileName,
452 boolean quoteFiles) {
453 String[] commandArray = null;
454 File tmpFile = null;
455
456 try {
457 /*
458 * Many system have been reported to get into trouble with
459 * long command lines - no, not only Windows ;-).
460 *
461 * POSIX seems to define a lower limit of 4k, so use a temporary
462 * file if the total length of the command line exceeds this limit.
463 */
464 if (Commandline.toString(args).length() > COMMAND_LINE_LIMIT
465 && firstFileName >= 0) {
466 PrintWriter out = null;
467 try {
468 tmpFile = FILE_UTILS.createTempFile(
469 "files", "", getJavac().getTempdir(), true, true);
470 out = new PrintWriter(new FileWriter(tmpFile));
471 for (int i = firstFileName; i < args.length; i++) {
472 if (quoteFiles && args[i].indexOf(" ") > -1) {
473 args[i] = args[i].replace(File.separatorChar, '/');
474 out.println("\"" + args[i] + "\"");
475 } else {
476 out.println(args[i]);
477 }
478 }
479 out.flush();
480 commandArray = new String[firstFileName + 1];
481 System.arraycopy(args, 0, commandArray, 0, firstFileName);
482 commandArray[firstFileName] = "@" + tmpFile;
483 } catch (IOException e) {
484 throw new BuildException("Error creating temporary file",
485 e, location);
486 } finally {
487 FileUtils.close(out);
488 }
489 } else {
490 commandArray = args;
491 }
492
493 try {
494 Execute exe = new Execute(
495 new LogStreamHandler(attributes,
496 Project.MSG_INFO,
497 Project.MSG_WARN));
498 if (Os.isFamily("openvms")) {
499 //Use the VM launcher instead of shell launcher on VMS
500 //for java
501 exe.setVMLauncher(true);
502 }
503 exe.setAntRun(project);
504 exe.setWorkingDirectory(project.getBaseDir());
505 exe.setCommandline(commandArray);
506 exe.execute();
507 return exe.getExitValue();
508 } catch (IOException e) {
509 throw new BuildException("Error running " + args[0]
510 + " compiler", e, location);
511 }
512 } finally {
513 if (tmpFile != null) {
514 tmpFile.delete();
515 }
516 }
517 }
518
519 /**
520 * Add extdirs to classpath
521 * @param classpath the classpath to use
522 * @deprecated since 1.5.x.
523 * Use org.apache.tools.ant.types.Path#addExtdirs instead.
524 */
525 protected void addExtdirsToClasspath(Path classpath) {
526 classpath.addExtdirs(extdirs);
527 }
528
529 /**
530 * Adds the command line arguments specific to the current implementation.
531 * @param cmd the command line to use
532 */
533 protected void addCurrentCompilerArgs(Commandline cmd) {
534 cmd.addArguments(getJavac().getCurrentCompilerArgs());
535 }
536
537 /**
538 * Shall we assume JDK 1.1 command line switches?
539 * @return true if jdk 1.1
540 * @since Ant 1.5
541 */
542 protected boolean assumeJava11() {
543 return "javac1.1".equals(attributes.getCompilerVersion());
544 }
545
546 /**
547 * Shall we assume JDK 1.2 command line switches?
548 * @return true if jdk 1.2
549 * @since Ant 1.5
550 */
551 protected boolean assumeJava12() {
552 return "javac1.2".equals(attributes.getCompilerVersion())
553 || ("classic".equals(attributes.getCompilerVersion())
554 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2))
555 || ("extJavac".equals(attributes.getCompilerVersion())
556 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2));
557 }
558
559 /**
560 * Shall we assume JDK 1.3 command line switches?
561 * @return true if jdk 1.3
562 * @since Ant 1.5
563 */
564 protected boolean assumeJava13() {
565 return "javac1.3".equals(attributes.getCompilerVersion())
566 || ("classic".equals(attributes.getCompilerVersion())
567 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3))
568 || ("modern".equals(attributes.getCompilerVersion())
569 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3))
570 || ("extJavac".equals(attributes.getCompilerVersion())
571 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3));
572 }
573
574 /**
575 * Shall we assume JDK 1.4 command line switches?
576 * @return true if jdk 1.4
577 * @since Ant 1.6.3
578 */
579 protected boolean assumeJava14() {
580 return "javac1.4".equals(attributes.getCompilerVersion())
581 || ("classic".equals(attributes.getCompilerVersion())
582 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_4))
583 || ("modern".equals(attributes.getCompilerVersion())
584 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_4))
585 || ("extJavac".equals(attributes.getCompilerVersion())
586 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_4));
587 }
588
589 /**
590 * Shall we assume JDK 1.5 command line switches?
591 * @return true if JDK 1.5
592 * @since Ant 1.6.3
593 */
594 protected boolean assumeJava15() {
595 return "javac1.5".equals(attributes.getCompilerVersion())
596 || ("classic".equals(attributes.getCompilerVersion())
597 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_5))
598 || ("modern".equals(attributes.getCompilerVersion())
599 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_5))
600 || ("extJavac".equals(attributes.getCompilerVersion())
601 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_5));
602 }
603
604 /**
605 * Shall we assume JDK 1.6 command line switches?
606 * @return true if JDK 1.6
607 * @since Ant 1.7
608 */
609 protected boolean assumeJava16() {
610 return "javac1.6".equals(attributes.getCompilerVersion())
611 || ("classic".equals(attributes.getCompilerVersion())
612 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_6))
613 || ("modern".equals(attributes.getCompilerVersion())
614 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_6))
615 || ("extJavac".equals(attributes.getCompilerVersion())
616 && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_6));
617 }
618
619 /**
620 * Combines a user specified bootclasspath with the system
621 * bootclasspath taking build.sysclasspath into account.
622 *
623 * @return a non-null Path instance that combines the user
624 * specified and the system bootclasspath.
625 */
626 protected Path getBootClassPath() {
627 Path bp = new Path(project);
628 if (bootclasspath != null) {
629 bp.append(bootclasspath);
630 }
631 return bp.concatSystemBootClasspath("ignore");
632 }
633
634 /**
635 * The argument the compiler wants to see if the debug attribute
636 * has been set to false.
637 *
638 * <p>A return value of <code>null</code> means no argument at all.</p>
639 *
640 * @return "-g:none" unless we expect to invoke a JDK 1.1 compiler.
641 *
642 * @since Ant 1.6.3
643 */
644 protected String getNoDebugArgument() {
645 return assumeJava11() ? null : "-g:none";
646 }
647 }
648