001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 1999 The Apache Software Foundation. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution, if
020: * any, must include the following acknowlegement:
021: * "This product includes software developed by the
022: * Apache Software Foundation (http://www.apache.org/)."
023: * Alternately, this acknowlegement may appear in the software itself,
024: * if and wherever such third-party acknowlegements normally appear.
025: *
026: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
027: * Foundation" must not be used to endorse or promote products derived
028: * from this software without prior written permission. For written
029: * permission, please contact apache@apache.org.
030: *
031: * 5. Products derived from this software may not be called "Apache"
032: * nor may "Apache" appear in their names without prior written
033: * permission of the Apache Group.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of the Apache Software Foundation. For more
051: * information on the Apache Software Foundation, please see
052: * <http://www.apache.org/>.
053: */
054:
055: package net.xoetrope.builder.editor.ant.taskdefs;
056:
057: import java.io.BufferedReader;
058: import java.io.BufferedWriter;
059: import java.io.File;
060: import java.io.FileInputStream;
061: import java.io.FileOutputStream;
062: import java.io.FileReader;
063: import java.io.FileWriter;
064: import java.io.FilenameFilter;
065: import java.io.IOException;
066: import java.util.Date;
067: import java.util.Enumeration;
068: import java.util.Hashtable;
069: import java.util.StringTokenizer;
070: import java.util.Vector;
071:
072: import net.xoetrope.builder.editor.XEditorProject;
073: import net.xoetrope.builder.editor.XEditorProjectManager;
074: import net.xoetrope.builder.editor.ant.BuildException;
075: import net.xoetrope.builder.editor.ant.DirectoryScanner;
076: import net.xoetrope.debug.DebugLogger;
077:
078: /**
079: * Task to compile Java source files. This task can take the following
080: * arguments:
081: * <ul>
082: * <li>sourcedir
083: * <li>destdir
084: * <li>deprecation
085: * <li>classpath
086: * <li>bootclasspath
087: * <li>extdirs
088: * <li>optimize
089: * <li>debug
090: * <li>target
091: * </ul>
092: * Of these arguments, the <b>sourcedir</b> and <b>destdir</b> are required.
093: * <p>
094: * When this task executes, it will recursively scan the sourcedir and
095: * destdir looking for Java source files to compile. This task makes its
096: * compile decision based on timestamp. Any other file in the
097: * sourcedir will be copied to the destdir allowing support files to be
098: * located properly in the classpath.
099: *
100: * @author James Davidson <a href="mailto:duncan@x180.com">duncan@x180.com</a>
101: *
102: * Modified for us in XuiEditor
103: */
104:
105: public class Javac extends MatchingTask {
106:
107: private File srcDir;
108: private File destDir;
109: private String compileClasspath;
110: private boolean debug = false;
111: private boolean optimize = false;
112: private boolean deprecation = false;
113: private boolean filtering = false;
114: private String target;
115: private String bootclasspath;
116: private String extdirs;
117:
118: protected Vector compileList = new Vector();
119: protected Hashtable filecopyList = new Hashtable();
120:
121: private File baseDir;
122: private static String javaVersion;
123:
124: public static final String JAVA_1_0 = "1.0";
125: public static final String JAVA_1_1 = "1.1";
126: public static final String JAVA_1_2 = "1.2";
127: public static final String JAVA_1_3 = "1.3";
128:
129: public Javac() {
130: detectJavaVersion();
131: }
132:
133: /**
134: * Set the source dir to find the source Java files.
135: */
136: public void setSrcDir(File srcDirName) {
137: srcDir = srcDirName;
138: }
139:
140: public void setSrcDir(String srcD) throws BuildException {
141: try {
142: setSrcDir(new File(new File(srcD).getCanonicalPath()));
143: } catch (IOException ioe) {
144: String msg = "Can't set srcDir " + srcDir + " due to "
145: + ioe.getMessage();
146: throw new BuildException(msg);
147: }
148: }
149:
150: /**
151: * Set the destination directory into which the Java source
152: * files should be compiled.
153: */
154: public void setDestDir(File destDirName) {
155: destDir = destDirName;
156: }
157:
158: public void setDestDir(String destD) throws BuildException {
159: try {
160: setDestDir(new File(new File(destD).getCanonicalPath()));
161: } catch (IOException ioe) {
162: String msg = "Can't set destdir " + destDir + " due to "
163: + ioe.getMessage();
164: throw new BuildException(msg);
165: }
166: }
167:
168: /**
169: * Set the classpath to be used for this compilation.
170: */
171: public void setClasspath(String classpath) {
172: compileClasspath = classpath;
173: }
174:
175: /**
176: * Sets the bootclasspath that will be used to compile the classes
177: * against.
178: */
179: public void setBootclasspath(String bootclasspath) {
180: this .bootclasspath = bootclasspath;
181: }
182:
183: /**
184: * Sets the extension directories that will be used during the
185: * compilation.
186: */
187: public void setExtdirs(String extdirs) {
188: this .extdirs = extdirs;
189: }
190:
191: /**
192: * Set the deprecation flag.
193: */
194: public void setDeprecation(boolean deprecationString) {
195: this .deprecation = deprecationString;
196: }
197:
198: /**
199: * Set the debug flag.
200: */
201: public void setDebug(boolean debugString) {
202: this .debug = debugString;
203: }
204:
205: /**
206: * Set the optimize flag.
207: */
208: public void setOptimize(boolean optimizeString) {
209: this .optimize = optimizeString;
210: }
211:
212: /**
213: * Sets the target VM that the classes will be compiled for. Valid
214: * strings are "1.1", "1.2", and "1.3".
215: */
216: public void setTarget(String target) {
217: this .target = target;
218: }
219:
220: /**
221: * Set the filtering flag.
222: */
223: public void setFiltering(boolean filter) {
224: filtering = filter;
225: }
226:
227: /**
228: * Executes the task.
229: */
230: public void execute() throws BuildException
231: {
232: // first off, make sure that we've got a srcdir and destdir
233:
234: if ( srcDir == null ) {
235: throw new BuildException( "srcdir attribute must be set!" );
236: }
237: if ( !srcDir.exists() ) {
238: throw new BuildException( "srcdir does not exist!" );
239: }
240: if ( destDir == null ) {
241: throw new BuildException( "destdir attribute must be set!" );
242: }
243:
244: // scan source and dest dirs to build up both copy lists and
245: // compile lists
246: DirectoryScanner ds = this .getDirectoryScanner( srcDir );
247:
248: String[] files = ds.getIncludedFiles();
249:
250: scanDir( srcDir, destDir, files );
251:
252: doClassicCompile();
253:
254: // copy the support files
255: if ( filecopyList.size() > 0 ) {
256: String msg = "Copying " + filecopyList.size() +
257: " support files to " + destDir.getAbsolutePath();
258: log( msg );
259: Enumeration enum = filecopyList.keys();
260: while ( enum.hasMoreElements() ) {
261: String fromFile = ( String )enum.nextElement();
262: String toFile = ( String )filecopyList.get( fromFile );
263: try {
264: copyFile( new File( fromFile ), new File( toFile ), filtering );
265: }
266: catch ( IOException ioe ) {
267: String errMsg = "Failed to copy " + fromFile + " to " + toFile
268: + " due to " + ioe.getMessage();
269: throw new BuildException( errMsg );
270: }
271: }
272: }
273: }
274:
275: /**
276: * Scans the directory looking for source files to be compiled and
277: * support files to be copied. The results are returned in the
278: * class variables compileList and filecopyList.
279: */
280:
281: protected void scanDir(File srcDir, File destDir, String files[]) {
282:
283: compileList.removeAllElements();
284: filecopyList.clear();
285:
286: long now = (new Date()).getTime();
287:
288: for (int i = 0; i < files.length; i++) {
289: File srcFile = new File(srcDir, files[i]);
290: if (files[i].endsWith(".java")) {
291: File classFile = new File(destDir, files[i].substring(
292: 0, files[i].indexOf(".java"))
293: + ".class");
294:
295: if (srcFile.lastModified() > now)
296: log("Warning: file modified in the future: "
297: + files[i]);
298:
299: if (srcFile.lastModified() > classFile.lastModified()) {
300: compileList.addElement(srcFile.getAbsolutePath());
301: }
302: } else {
303: File destFile = new File(destDir, files[i]);
304: if (srcFile.lastModified() > destFile.lastModified()) {
305: filecopyList.put(srcFile.getAbsolutePath(),
306: destFile.getAbsolutePath());
307: }
308: }
309: }
310: }
311:
312: /**
313: * Builds the compilation classpath.
314: */
315:
316: // XXX
317: // we need a way to not use the current classpath.
318: private String getCompileClasspath() {
319: StringBuffer classpath = new StringBuffer();
320:
321: // add dest dir to classpath so that previously compiled and
322: // untouched classes are on classpath
323:
324: //classpath.append(sourceDir.getAbsolutePath());
325: //classpath.append(File.pathSeparator);
326: classpath.append(destDir.getAbsolutePath());
327:
328: // add our classpath to the mix
329:
330: if (compileClasspath != null)
331: addExistingToClasspath(classpath, compileClasspath);
332:
333: // add the system classpath
334: addExistingToClasspath(classpath, System
335: .getProperty("java.class.path"));
336:
337: // add the XUI library jar to the file for Runtime
338: String xuiLib = System.getProperty("user.dir")
339: + File.separatorChar;
340: if (((XEditorProject) XEditorProjectManager.getCurrentProject())
341: .isSwingClient())
342: xuiLib += "XuiCoreSwing.jar";
343: else
344: xuiLib += "XuiCore.jar";
345: addExistingToClasspath(classpath, xuiLib);
346:
347: xuiLib = ((XEditorProject) XEditorProjectManager
348: .getCurrentProject()).getPath()
349: + File.separatorChar + "lib" + File.separatorChar;
350: if (((XEditorProject) XEditorProjectManager.getCurrentProject())
351: .isSwingClient())
352: xuiLib += "XuiCoreSwing.jar";
353: else
354: xuiLib += "XuiCore.jar";
355: addExistingToClasspath(classpath, xuiLib);
356:
357: return classpath.toString();
358: }
359:
360: /**
361: * Takes a classpath-like string, and adds each element of
362: * this string to a new classpath, if the components exist.
363: * Components that don't exist, aren't added.
364: * We do this, because jikes issues warnings for non-existant
365: * files/dirs in his classpath, and these warnings are pretty
366: * annoying.
367: * @param target - target classpath
368: * @param source - source classpath
369: * to get file objects.
370: */
371: private void addExistingToClasspath(StringBuffer target,
372: String source) {
373: StringTokenizer tok = new StringTokenizer(source, System
374: .getProperty("path.separator"), false);
375: while (tok.hasMoreTokens()) {
376: File f = resolveFile(tok.nextToken());
377:
378: if (f.exists()) {
379: target.append(File.pathSeparator);
380: target.append(f.getAbsolutePath());
381: } else
382: log("Dropping from classpath: " + f.getAbsolutePath());
383: }
384: }
385:
386: /**
387: * Peforms a copmile using the classic compiler that shipped with
388: * JDK 1.1 and 1.2.
389: */
390:
391: private void doClassicCompile() throws BuildException
392: {
393: log( "Using classic compiler" );
394: String classpath = getCompileClasspath();
395: Vector argList = new Vector();
396:
397: if ( deprecation == true )
398: argList.addElement( "-deprecation" );
399:
400: argList.addElement( "-d" );
401: argList.addElement( destDir.getAbsolutePath() );
402: argList.addElement( "-classpath" );
403: // Just add "sourcepath" to classpath ( for JDK1.1 )
404: if ( javaVersion.startsWith( "1.1" ) ) {
405: argList.addElement( classpath + File.pathSeparator +
406: srcDir.getAbsolutePath() );
407: }
408: else {
409: argList.addElement( classpath );
410: argList.addElement( "-sourcepath" );
411: argList.addElement( srcDir.getAbsolutePath() );
412: if ( target != null ) {
413: argList.addElement( "-target" );
414: argList.addElement( target );
415: }
416: }
417: if ( debug ) {
418: argList.addElement( "-g" );
419: }
420: if ( optimize ) {
421: argList.addElement( "-O" );
422: }
423: if ( bootclasspath != null ) {
424: argList.addElement( "-bootclasspath" );
425: argList.addElement( bootclasspath );
426: }
427: if ( extdirs != null ) {
428: argList.addElement( "-extdirs" );
429: argList.addElement( extdirs );
430: }
431:
432: log( "Compilation args: " + argList.toString() );
433:
434: String[] args = new String[ argList.size() + compileList.size() ];
435: int counter = 0;
436:
437: for ( int i = 0; i < argList.size(); i++ ) {
438: args[ i ] = ( String )argList.elementAt( i );
439: counter++;
440: }
441:
442: // XXX
443: // should be using system independent line feed!
444:
445: StringBuffer niceSourceList = new StringBuffer( "Files to be compiled:"
446: + "\r\n" );
447:
448: Enumeration enum = compileList.elements();
449: while ( enum.hasMoreElements() ) {
450: args[ counter ] = ( String )enum.nextElement();
451: niceSourceList.append( " " + args[ counter ] + "\r\n" );
452: counter++;
453: }
454:
455: log( niceSourceList.toString() );
456:
457: // XXX
458: // provide the compiler a different message sink - namely our own
459:
460: JavacOutputStream jos = new JavacOutputStream();
461:
462: sun.tools.javac.Main compiler = new sun.tools.javac.Main( jos, "javac" );
463: compiler.compile( args );
464: if ( jos.getErrorFlag() ) {
465: String msg = "Compile failed, messages should have been provided.";
466: throw new BuildException( msg );
467: }
468: }
469:
470: class JarFilenameFilter implements FilenameFilter {
471: public boolean accept(File dir, String name) {
472: return name.endsWith(".jar");
473: }
474: }
475:
476: /**
477: * Emulation of extdirs feature in java >= 1.2.
478: * This method adds all jar archives in the given
479: * directories (but not in sub-directories!) to the classpath,
480: * so that you don't have to specify them all one by one.
481: * @param classpath - stringbuffer to append jar files to
482: */
483: private void addExtdirsToClasspath(StringBuffer classpath) {
484: // FIXME
485: // Should we scan files recursively? How does
486: // javac handle this?
487:
488: if (extdirs != null) {
489: StringTokenizer tok = new StringTokenizer(extdirs,
490: File.pathSeparator, false);
491: while (tok.hasMoreTokens()) {
492: File dir = resolveFile(tok.nextToken());
493: String[] files = dir.list(new JarFilenameFilter());
494: for (int i = 0; i < files.length; i++) {
495: File f = new File(dir, files[i]);
496: if (f.exists() && f.isFile()) {
497: classpath.append(File.pathSeparator);
498: classpath.append(f.getAbsolutePath());
499: }
500: }
501: }
502: }
503: }
504:
505: public File resolveFile(String fileName) {
506: // deal with absolute files
507: if (fileName.startsWith("/"))
508: return new File(fileName);
509: if (fileName.startsWith(System.getProperty("file.separator")))
510: return new File(fileName);
511:
512: // Eliminate consecutive slashes after the drive spec
513: if (fileName.length() >= 2
514: && Character.isLetter(fileName.charAt(0))
515: && fileName.charAt(1) == ':') {
516: char[] ca = fileName.replace('/', '\\').toCharArray();
517: char c;
518: StringBuffer sb = new StringBuffer();
519:
520: for (int i = 0; i < ca.length; i++) {
521: if ((ca[i] != '\\')
522: || (ca[i] == '\\' && i > 0 && ca[i - 1] != '\\')) {
523: if (i == 0 && Character.isLetter(ca[i])
524: && i < ca.length - 1 && ca[i + 1] == ':') {
525: c = Character.toUpperCase(ca[i]);
526: } else {
527: c = ca[i];
528: }
529:
530: sb.append(c);
531: }
532: }
533:
534: return new File(sb.toString());
535: }
536:
537: File file = new File(baseDir.getAbsolutePath());
538: StringTokenizer tok = new StringTokenizer(fileName, "/", false);
539: while (tok.hasMoreTokens()) {
540: String part = tok.nextToken();
541: if (part.equals("..")) {
542: file = new File(file.getParent());
543: } else if (part.equals(".")) {
544: // Do nothing here
545: } else {
546: file = new File(file, part);
547: }
548: }
549:
550: try {
551: return new File(file.getCanonicalPath());
552: } catch (IOException e) {
553: logError("IOException getting canonical path for " + file
554: + ": " + e.getMessage());
555: return new File(file.getAbsolutePath());
556: }
557: }
558:
559: // match basedir attribute in xml
560: public void setBaseDir(String baseD) throws BuildException {
561: try {
562: setBaseDir(new File(new File(baseD).getCanonicalPath()));
563: } catch (IOException ioe) {
564: String msg = "Can't set basedir " + baseDir + " due to "
565: + ioe.getMessage();
566: throw new BuildException(msg);
567: }
568: }
569:
570: public void setBaseDir(File baseDir) {
571: this .baseDir = baseDir;
572: String msg = "Project base dir set to: " + baseDir;
573: log(msg);
574: }
575:
576: private void detectJavaVersion() {
577: // Determine the Java version by looking at available classes
578: // java.lang.StrictMath was introduced in JDK 1.3
579: // java.lang.ThreadLocal was introduced in JDK 1.2
580: // java.lang.Void was introduced in JDK 1.1
581: // Count up version until a NoClassDefFoundError ends the try
582: try {
583: javaVersion = JAVA_1_0;
584: Class.forName("java.lang.Void");
585: javaVersion = JAVA_1_1;
586: Class.forName("java.lang.ThreadLocal");
587: javaVersion = JAVA_1_2;
588: Class.forName("java.lang.StrictMath");
589: javaVersion = JAVA_1_3;
590: } catch (ClassNotFoundException cnfe) {
591: // swallow as we've hit the max class version that
592: // we have
593: }
594:
595: // sanity check
596: if (javaVersion == JAVA_1_0) {
597: throw new BuildException("Ant cannot work on Java 1.0");
598: }
599:
600: log("Detected Java Version: " + javaVersion);
601:
602: log("Detected OS: " + System.getProperty("os.name"));
603: }
604:
605: /**
606: * Convienence method to copy a file from a source to a destination
607: * specifying if token filtering must be used.
608: *
609: * @throws IOException
610: */
611: public void copyFile(File sourceFile, File destFile,
612: boolean filtering) throws IOException {
613:
614: if (destFile.lastModified() < sourceFile.lastModified()) {
615: log("Copy: " + sourceFile.getAbsolutePath() + " > "
616: + destFile.getAbsolutePath());
617:
618: // ensure that parent dir of dest file exists!
619: // not using getParentFile method to stay 1.1 compat
620: File parent = new File(destFile.getParent());
621: if (!parent.exists()) {
622: parent.mkdirs();
623: }
624:
625: if (filtering) {
626: BufferedReader in = new BufferedReader(new FileReader(
627: sourceFile));
628: BufferedWriter out = new BufferedWriter(new FileWriter(
629: destFile));
630:
631: int length;
632: String newline = null;
633: String line = in.readLine();
634: while (line != null) {
635: if (line.length() == 0) {
636: out.newLine();
637: } else {
638: // newline = replace( line, filters );
639: out.write(newline);
640: out.newLine();
641: }
642: line = in.readLine();
643: }
644:
645: out.close();
646: in.close();
647: } else {
648: FileInputStream in = new FileInputStream(sourceFile);
649: FileOutputStream out = new FileOutputStream(destFile);
650:
651: byte[] buffer = new byte[8 * 1024];
652: int count = 0;
653: do {
654: out.write(buffer, 0, count);
655: count = in.read(buffer, 0, buffer.length);
656: } while (count != -1);
657:
658: in.close();
659: out.close();
660: }
661: }
662: }
663:
664: private void log(String s) {
665: DebugLogger.log(s);
666: }
667:
668: private void logError(String s) {
669: DebugLogger.logError(s);
670: }
671: }
|