| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 1999 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, if |
| * any, must include the following acknowlegement: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowlegement may appear in the software itself, |
| * if and wherever such third-party acknowlegements normally appear. |
| * |
| * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software |
| * Foundation" must not be used to endorse or promote products derived |
| * from this software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache" |
| * nor may "Apache" appear in their names without prior written |
| * permission of the Apache Group. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| package org.apache.tools.ant.taskdefs.optional; |
| |
| |
| |
| |
| |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.PrintStream; |
| import java.util.Date; |
| import java.util.Properties; |
| import org.apache.tools.ant.BuildEvent; |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.BuildListener; |
| import org.apache.tools.ant.DirectoryScanner; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.taskdefs.Java; |
| import org.apache.tools.ant.taskdefs.Javac; |
| import org.apache.tools.ant.taskdefs.MatchingTask; |
| import org.apache.tools.ant.taskdefs.Mkdir; |
| import org.apache.tools.ant.taskdefs.compilers.DefaultCompilerAdapter; |
| import org.apache.tools.ant.types.FileSet; |
| import org.apache.tools.ant.types.Path; |
| import org.apache.tools.ant.types.Reference; |
| |
| /** |
| * Instruments Java classes with <a href="http://www.reliable-systems.com/tools/">iContract</a> |
| * DBC preprocessor. |
| * <br/> |
| * The task can generate a properties file for <a href="http://hjem.sol.no/hellesoy/icontrol.html">iControl</a>, |
| * a graphical user interface that lets you turn on/off assertions. iControl generates a control file that you can refer to |
| * from this task using the controlfile attribute. |
| * <p/> |
| * Thanks to Rainer Schmitz for enhancements and comments. |
| * |
| * @author <a href="mailto:aslak.hellesoy@bekk.no">Aslak Hellesøy</a> |
| * |
| * <p/> |
| * <table border="1" cellpadding="2" cellspacing="0"> |
| * <tr> |
| * <td valign="top"><b>Attribute</b></td> |
| * <td valign="top"><b>Description</b></td> |
| * <td align="center" valign="top"><b>Required</b></td> |
| * </tr> |
| * <tr> |
| * <td valign="top">srcdir</td> |
| * <td valign="top">Location of the java files.</td> |
| * <td valign="top" align="center">Yes</td> |
| * </tr> |
| * <tr> |
| * <td valign="top">instrumentdir</td> |
| * <td valign="top">Indicates where the instrumented source files should go.</td> |
| * <td valign="top" align="center">Yes</td> |
| * </tr> |
| * <tr> |
| * <td valign="top">repositorydir</td> |
| * <td valign="top">Indicates where the repository source files should go.</td> |
| * <td valign="top" align="center">Yes</td> |
| * </tr> |
| * <tr> |
| * <td valign="top">builddir</td> |
| * <td valign="top">Indicates where the compiled instrumented classes should go. |
| * Defaults to the value of instrumentdir. |
| * </p> |
| * <em>NOTE:</em> Don't use the same directory for compiled instrumented classes |
| * and uninstrumented classes. It will break the dependency checking. (Classes will |
| * not be reinstrumented if you change them).</td> |
| * <td valign="top" align="center">No</td> |
| * </tr> |
| * <tr> |
| * <td valign="top">repositorybuilddir</td> |
| * <td valign="top">Indicates where the compiled repository classes should go. |
| * Defaults to the value of repositorydir.</td> |
| * <td valign="top" align="center">No</td> |
| * </tr> |
| * <tr> |
| * <td valign="top">pre</td> |
| * <td valign="top">Indicates whether or not to instrument for preconditions. |
| * Defaults to <code>true</code> unless controlfile is specified, in which case it |
| * defaults to <code>false</code>.</td> |
| * <td valign="top" align="center">No</td> |
| * </tr> |
| * <tr> |
| * <td valign="top">post</td> |
| * <td valign="top">Indicates whether or not to instrument for postconditions. |
| * Defaults to <code>true</code> unless controlfile is specified, in which case it |
| * defaults to <code>false</code>.</td> |
| * <td valign="top" align="center">No</td> |
| * </tr> |
| * <tr> |
| * <td valign="top">invariant</td> |
| * <td valign="top">Indicates whether or not to instrument for invariants. |
| * Defaults to <code>true</code> unless controlfile is specified, in which case it |
| * defaults to <code>false</code>.</td> |
| * <td valign="top" align="center">No</td> |
| * </tr> |
| * <tr> |
| * <td valign="top">failthrowable</td> |
| * <td valign="top">The full name of the Throwable (Exception) that should be |
| * thrown when an assertion is violated. Defaults to <code>java.lang.Error</code></td> |
| * <td valign="top" align="center">No</td> |
| * </tr> |
| * <tr> |
| * <td valign="top">verbosity</td> |
| * <td valign="top">Indicates the verbosity level of iContract. Any combination |
| * of <code>error*,warning*,note*,info*,progress*,debug*</code> (comma separated) can be |
| * used. Defaults to <code>error*</code></td> |
| * <td valign="top" align="center">No</td> |
| * </tr> |
| * <tr> |
| * <td valign="top">quiet</td> |
| * <td valign="top">Indicates if iContract should be quiet. Turn it off if many your classes extend uninstrumented classes |
| * and you don't want warnings about this. Defaults to <code>false</code></td> |
| * <td valign="top" align="center">No</td> |
| * </tr> |
| * <tr> |
| * <td valign="top">updateicontrol</td> |
| * <td valign="top">If set to true, it indicates that the properties file for |
| * iControl in the current directory should be updated (or created if it doesn't exist). |
| * Defaults to <code>false</code>.</td> |
| * <td valign="top" align="center">No</td> |
| * </tr> |
| * <tr> |
| * <td valign="top">controlfile</td> |
| * <td valign="top">The name of the control file to pass to iContract. Consider using iControl to generate the file. |
| * Default is not to pass a file. </td> |
| * <td valign="top" align="center">Only if <code>updateicontrol=true</code></td> |
| * </tr> |
| * <tr> |
| * <td valign="top">classdir</td> |
| * <td valign="top">Indicates where compiled (unistrumented) classes are located. |
| * This is required in order to properly update the icontrol.properties file, not |
| * for instrumentation.</td> |
| * <td valign="top" align="center">Only if <code>updateicontrol=true</code></td> |
| * </tr> |
| * <tr> |
| * <td valign="top">targets</td> |
| * <td valign="top">Name of the file that will be generated by this task, which lists all the |
| * classes that iContract will instrument. If specified, the file will not be deleted after execution. |
| * If not specified, a file will still be created, but it will be deleted after execution.</td> |
| * <td valign="top" align="center">No</td> |
| * </tr> |
| * </table> |
| * |
| * <p/> |
| * <b>Note:</b> iContract will use the java compiler indicated by the project's |
| * <code>build.compiler</code> property. See documentation of the Javac task for |
| * more information. |
| * <p/> |
| * Nested includes and excludes are also supported. |
| * |
| * <p><b>Example:</b></p> |
| * <pre> |
| * <icontract |
| * srcdir="${build.src}" |
| * instrumentdir="${build.instrument}" |
| * repositorydir="${build.repository}" |
| * builddir="${build.instrclasses}" |
| * updateicontrol="true" |
| * classdir="${build.classes}" |
| * controlfile="control" |
| * targets="targets" |
| * verbosity="error*,warning*" |
| * quiet="true" |
| * > |
| * <classpath refid="compile-classpath"/> |
| * </icontract> |
| * </pre> |
| * |
| */ |
| public class IContract extends MatchingTask { |
| |
| private static final String ICONTROL_PROPERTIES_HEADER = |
| " You might want to set classRoot to point to your normal compilation class root directory."; |
| |
| private static final String ICONTROL_PROPERTIES_MESSAGE = |
| "You should probably modify icontrol.properties' classRoot to where comiled (uninstrumented) classes go."; |
| |
| /** \ on windows, / on linux/unix */ |
| private static final String ps = System.getProperty( "path.separator" ); |
| |
| /** compiler to use for instrumenation */ |
| private String icCompiler = "javac"; |
| |
| /** temporary file with file names of all java files to be instrumented */ |
| private File targets = null; |
| |
| /** will be set to true if any of the sourca files are newer than the instrumented files */ |
| private boolean dirty = false; |
| |
| /** set to true if the iContract jar is missing */ |
| private boolean iContractMissing = false; |
| |
| /** source file root */ |
| private File srcDir = null; |
| |
| /** instrumentation src root */ |
| private File instrumentDir = null; |
| |
| /** instrumentation build root */ |
| private File buildDir = null; |
| |
| /** repository src root */ |
| private File repositoryDir = null; |
| |
| /** repository build root */ |
| private File repBuildDir = null; |
| |
| /** classpath */ |
| private Path classpath = null; |
| |
| /** The class of the Throwable to be thrown on failed assertions */ |
| private String failThrowable = "java.lang.Error"; |
| |
| /** The -v option */ |
| private String verbosity = "error*"; |
| |
| /** The -q option */ |
| private boolean quiet = false; |
| |
| /** Indicates whether or not to use internal compilation */ |
| private boolean internalcompilation = false; |
| |
| /** The -m option */ |
| private File controlFile = null; |
| |
| /** Indicates whether or not to instrument for preconditions */ |
| private boolean pre = true; |
| private boolean preModified = false; |
| |
| /** Indicates whether or not to instrument for postconditions */ |
| private boolean post = true; |
| private boolean postModified = false; |
| |
| /** Indicates whether or not to instrument for invariants */ |
| private boolean invariant = true; |
| private boolean invariantModified = false; |
| |
| /** Indicates whether or not to instrument all files regardless of timestamp */ |
| // can't be explicitly set, is set if control file exists and is newer than any source file |
| private boolean instrumentall = false; |
| |
| /** |
| * Indicates the name of a properties file (intentionally for iControl) where the classpath |
| * property should be updated. |
| */ |
| private boolean updateIcontrol = false; |
| |
| /** |
| * Regular compilation class root |
| */ |
| private File classDir = null; |
| |
| /** |
| * Sets the source directory |
| * |
| * @param srcDir the source directory |
| */ |
| public void setSrcdir( File srcDir ) { |
| this.srcDir = srcDir; |
| } |
| |
| /** |
| * Sets the class directory (uninstrumented classes) |
| * |
| * @param srcDir the source directory |
| */ |
| public void setClassdir( File classDir ) { |
| this.classDir = classDir; |
| } |
| |
| /** |
| * Sets the instrumentation directory |
| * |
| * @param instrumentDir the source directory |
| */ |
| public void setInstrumentdir( File instrumentDir ) { |
| this.instrumentDir = instrumentDir; |
| if ( this.buildDir == null ) { |
| setBuilddir( instrumentDir ); |
| } |
| } |
| |
| /** |
| * Sets the build directory for instrumented classes |
| * |
| * @param buildDir the build directory |
| */ |
| public void setBuilddir( File buildDir ) { |
| this.buildDir = buildDir; |
| } |
| |
| /** |
| * Sets the build directory for repository classes |
| * |
| * @param repositoryDir the source directory |
| */ |
| public void setRepositorydir( File repositoryDir ) { |
| this.repositoryDir = repositoryDir; |
| if( this.repBuildDir == null ) { |
| setRepbuilddir( repositoryDir ); |
| } |
| } |
| |
| /** |
| * Sets the build directory for instrumented classes |
| * |
| * @param buildDir the build directory |
| */ |
| public void setRepbuilddir( File repBuildDir ) { |
| this.repBuildDir = repBuildDir; |
| } |
| |
| /** |
| * Turns on/off precondition instrumentation |
| * |
| * @param pre true turns it on |
| */ |
| public void setPre( boolean pre ) { |
| this.pre = pre; |
| preModified = true; |
| } |
| |
| /** |
| * Turns on/off postcondition instrumentation |
| * |
| * @param post true turns it on |
| */ |
| public void setPost( boolean post ) { |
| this.post = post; |
| postModified = true; |
| } |
| |
| /** |
| * Turns on/off invariant instrumentation |
| * |
| * @param invariant true turns it on |
| */ |
| public void setInvariant( boolean invariant ) { |
| this.invariant = invariant; |
| invariantModified = true; |
| } |
| |
| /** |
| * Sets the Throwable (Exception) to be thrown on assertion violation |
| * |
| * @param clazz the fully qualified Throwable class name |
| */ |
| public void setFailthrowable( String clazz ) { |
| this.failThrowable = clazz; |
| } |
| |
| /** |
| * Sets the verbosity level of iContract. Any combination of |
| * error*,warning*,note*,info*,progress*,debug* (comma separated) |
| * can be used. Defaults to error*,warning* |
| * |
| * @param verbosity verbosity level |
| */ |
| public void setVerbosity( String verbosity ) { |
| this.verbosity = verbosity; |
| } |
| |
| /** |
| * Tells iContract to be quiet. |
| * |
| * @param quiet true if iContract should be quiet. |
| */ |
| public void setQuiet( boolean quiet ) { |
| this.quiet = quiet; |
| } |
| |
| /** |
| * Sets the name of the file where targets will be written. |
| * That is the file that tells iContract what files to process. |
| * |
| * @param targets the targets file name |
| */ |
| public void setTargets( File targets ) { |
| this.targets = targets; |
| } |
| |
| /** |
| * Sets the control file to pass to iContract. |
| * |
| * @param controlFile the control file |
| */ |
| public void setControlfile( File controlFile ) { |
| if( !controlFile.exists() ) { |
| log( "WARNING: Control file " + controlFile.getAbsolutePath() + " doesn't exist. iContract will be run without control file." ); |
| } |
| this.controlFile = controlFile; |
| } |
| |
| /** |
| * Sets the classpath to be used for invocation of iContract. |
| * |
| * @path the classpath |
| */ |
| public void setClasspath( Path path ) { |
| createClasspath().append( path ); |
| } |
| |
| /** |
| * Creates a nested classpath element |
| * |
| * @return the nested classpath element |
| */ |
| public Path createClasspath() { |
| if (classpath == null) { |
| classpath = new Path( getProject() ); |
| } |
| return classpath; |
| } |
| |
| /** |
| * Adds a reference to a classpath defined elsewhere. |
| * |
| * @param reference referenced classpath |
| */ |
| public void setClasspathRef( Reference reference ) { |
| createClasspath().setRefid( reference ); |
| } |
| |
| /** |
| * Decides whether or not to update iControl properties file |
| * |
| * @param updateIcontrol true if iControl properties file should be updated |
| */ |
| public void setUpdateicontrol( boolean updateIcontrol ) { |
| this.updateIcontrol = updateIcontrol; |
| } |
| |
| /** |
| * Executes the task |
| * |
| * @exception BuildException if the instrumentation fails |
| */ |
| public void execute() throws BuildException { |
| preconditions(); |
| scan(); |
| if( dirty ) { |
| |
| // turn off assertions if we're using controlfile, unless they are not explicitly set. |
| boolean useControlFile = (controlFile != null) && controlFile.exists(); |
| if( useControlFile && !preModified ) { |
| pre = false; |
| } |
| if( useControlFile && !postModified ) { |
| post = false; |
| } |
| if( useControlFile && !invariantModified ) { |
| invariant = false; |
| } |
| // issue warning if pre,post or invariant is used together with controlfile |
| if( ( pre || post || invariant ) && controlFile != null ) { |
| log( "WARNING: specifying pre,post or invariant will override control file settings" ); |
| } |
| |
| |
| |
| // We want to be notified if iContract jar is missing. This makes life easier for the user |
| // who didn't understand that iContract is a separate library (duh!) |
| getProject().addBuildListener( new IContractPresenceDetector() ); |
| |
| // Prepare the directories for iContract. iContract will make them if they |
| // don't exist, but for some reason I don't know, it will complain about the REP files |
| // afterwards |
| Mkdir mkdir = (Mkdir) project.createTask( "mkdir" ); |
| mkdir.setDir( instrumentDir ); |
| mkdir.execute(); |
| mkdir.setDir( buildDir ); |
| mkdir.execute(); |
| mkdir.setDir( repositoryDir ); |
| mkdir.execute(); |
| |
| // Set the classpath that is needed for regular Javac compilation |
| Path baseClasspath = createClasspath(); |
| |
| // Might need to add the core classes if we're not using Sun's Javac (like Jikes) |
| String compiler = project.getProperty("build.compiler"); |
| ClasspathHelper classpathHelper = new ClasspathHelper( compiler ); |
| classpathHelper.modify( baseClasspath ); |
| |
| // Create the classpath required to compile the sourcefiles BEFORE instrumentation |
| Path beforeInstrumentationClasspath = ((Path) baseClasspath.clone()); |
| beforeInstrumentationClasspath.append( new Path( getProject(), srcDir.getAbsolutePath() ) ); |
| |
| // Create the classpath required to compile the sourcefiles AFTER instrumentation |
| Path afterInstrumentationClasspath = ((Path) baseClasspath.clone()); |
| afterInstrumentationClasspath.append( new Path( getProject(), instrumentDir.getAbsolutePath() ) ); |
| afterInstrumentationClasspath.append( new Path( getProject(), repositoryDir.getAbsolutePath() ) ); |
| afterInstrumentationClasspath.append( new Path( getProject(), srcDir.getAbsolutePath() ) ); |
| afterInstrumentationClasspath.append( new Path( getProject(), buildDir.getAbsolutePath() ) ); |
| |
| // Create the classpath required to automatically compile the repository files |
| Path repositoryClasspath = ((Path) baseClasspath.clone()); |
| repositoryClasspath.append( new Path( getProject(), instrumentDir.getAbsolutePath() ) ); |
| repositoryClasspath.append( new Path( getProject(), srcDir.getAbsolutePath() ) ); |
| repositoryClasspath.append( new Path( getProject(), repositoryDir.getAbsolutePath() ) ); |
| repositoryClasspath.append( new Path( getProject(), buildDir.getAbsolutePath() ) ); |
| |
| // Create the classpath required for iContract itself |
| Path iContractClasspath = ((Path) baseClasspath.clone()); |
| iContractClasspath.append( new Path( getProject(), System.getProperty( "java.home" ) + File.separator + ".." + File.separator + "lib" + File.separator + "tools.jar" ) ); |
| iContractClasspath.append( new Path( getProject(), srcDir.getAbsolutePath() ) ); |
| iContractClasspath.append( new Path( getProject(), repositoryDir.getAbsolutePath() ) ); |
| iContractClasspath.append( new Path( getProject(), instrumentDir.getAbsolutePath() ) ); |
| iContractClasspath.append( new Path( getProject(), buildDir.getAbsolutePath() ) ); |
| |
| // Create a forked java process |
| Java iContract = (Java) project.createTask( "java" ); |
| iContract.setTaskName( getTaskName() ); |
| iContract.setFork( true ); |
| iContract.setClassname( "com.reliablesystems.iContract.Tool" ); |
| iContract.setClasspath( iContractClasspath ); |
| |
| // Build the arguments to iContract |
| StringBuffer args = new StringBuffer(); |
| args.append( directiveString() ); |
| args.append( "-v" ).append( verbosity ).append( " " ); |
| args.append( "-b" ).append( "\"" ).append( icCompiler ).append( " -classpath " ).append( beforeInstrumentationClasspath ).append( "\" " ); |
| args.append( "-c" ).append( "\"" ).append( icCompiler ).append( " -classpath " ).append( afterInstrumentationClasspath ).append( " -d " ).append( buildDir ).append( "\" " ); |
| args.append( "-n" ).append( "\"" ).append( icCompiler ).append( " -classpath " ).append( repositoryClasspath ).append( "\" " ); |
| args.append( "-d" ).append( failThrowable ).append( " " ); |
| args.append( "-o" ).append( instrumentDir ).append( File.separator ).append( "@p" ).append( File.separator ).append( "@f.@e " ); |
| args.append( "-k" ).append( repositoryDir ).append( File.separator ).append( "@p " ); |
| args.append( quiet ? "-q " : "" ); |
| args.append( instrumentall ? "-a " : "" ); // reinstrument everything if controlFile exists and is newer than any class |
| args.append( "@" ).append( targets.getAbsolutePath() ); |
| iContract.createArg().setLine( args.toString() ); |
| |
| //System.out.println( "JAVA -classpath " + iContractClasspath + " com.reliablesystems.iContract.Tool " + args.toString() ); |
| |
| // update iControlProperties if it's set. |
| if( updateIcontrol ) { |
| Properties iControlProps = new Properties(); |
| try { // to read existing propertiesfile |
| iControlProps.load( new FileInputStream( "icontrol.properties" ) ); |
| } catch( IOException e ) { |
| log( "File icontrol.properties not found. That's ok. Writing a default one." ); |
| } |
| iControlProps.setProperty( "sourceRoot", srcDir.getAbsolutePath() ); |
| iControlProps.setProperty( "classRoot", classDir.getAbsolutePath() ); |
| iControlProps.setProperty( "classpath", afterInstrumentationClasspath.toString() ); |
| iControlProps.setProperty( "controlFile", controlFile.getAbsolutePath() ); |
| iControlProps.setProperty( "targetsFile", targets.getAbsolutePath() ); |
| |
| try { // to read existing propertiesfile |
| iControlProps.store( new FileOutputStream( "icontrol.properties" ), ICONTROL_PROPERTIES_HEADER ); |
| log( "Updated icontrol.properties" ); |
| } catch( IOException e ) { |
| log( "Couldn't write icontrol.properties." ); |
| } |
| } |
| |
| // do it! |
| int result = iContract.executeJava(); |
| if( result != 0 ) { |
| if( iContractMissing ) { |
| log( "iContract can't be found on your classpath. Your classpath is:" ); |
| log( classpath.toString() ); |
| log( "If you don't have the iContract jar, go get it at http://www.reliable-systems.com/tools/" ); |
| } |
| throw new BuildException( "iContract instrumentation failed. Code=" + result ); |
| } |
| |
| } else { // not dirty |
| //log( "Nothing to do. Everything up to date." ); |
| } |
| } |
| |
| /** |
| * Checks that the required attributes are set. |
| */ |
| private void preconditions() throws BuildException { |
| if (srcDir == null) { |
| throw new BuildException( "srcdir attribute must be set!", location ); |
| } |
| if (!srcDir.exists()) { |
| throw new BuildException( "srcdir \"" + srcDir.getPath() + "\" does not exist!", location ); |
| } |
| if (instrumentDir == null) { |
| throw new BuildException( "instrumentdir attribute must be set!", location ); |
| } |
| if (repositoryDir == null) { |
| throw new BuildException( "repositorydir attribute must be set!", location ); |
| } |
| if (updateIcontrol == true && classDir == null) { |
| throw new BuildException( "classdir attribute must be specified when updateicontrol=true!", location ); |
| } |
| if( updateIcontrol == true && controlFile == null ) { |
| throw new BuildException( "controlfile attribute must be specified when updateicontrol=true!", location ); |
| } |
| } |
| |
| /** |
| * Verifies whether any of the source files have changed. Done by comparing date of source/class files. |
| * The whole lot is "dirty" if at least one source file or the control file is newer than the instrumented |
| * files. If not dirty, iContract will not be executed. |
| * <br/> |
| * Also creates a temporary file with a list of the source files, that will be deleted upon exit. |
| */ |
| private void scan() throws BuildException { |
| long now = (new Date()).getTime(); |
| |
| DirectoryScanner ds = null; |
| |
| ds = getDirectoryScanner( srcDir ); |
| String[] files = ds.getIncludedFiles(); |
| |
| FileOutputStream targetOutputStream = null; |
| PrintStream targetPrinter = null; |
| boolean writeTargets = false; |
| try { |
| if( targets == null ) { |
| targets = new File( "targets" ); |
| log( "Warning: targets file not specified. generating file: " + targets.getName() ); |
| writeTargets = true; |
| } else if( !targets.exists() ) { |
| log( "Specified targets file doesn't exist. generating file: " + targets.getName() ); |
| writeTargets = true; |
| } |
| if( writeTargets ) { |
| log( "You should consider using iControl to create a target file." ); |
| targetOutputStream = new FileOutputStream( targets ); |
| targetPrinter = new PrintStream( targetOutputStream ); |
| } |
| for (int i = 0; i < files.length; i++ ) { |
| File srcFile = new File(srcDir, files[i]); |
| if (files[i].endsWith(".java")) { |
| // print the target, while we're at here. (Only if generatetarget=true). |
| if( targetPrinter != null ) { |
| targetPrinter.println( srcFile.getAbsolutePath() ); |
| } |
| File classFile = new File( buildDir, files[i].substring( 0, files[i].indexOf( ".java" ) ) + ".class" ); |
| |
| if (srcFile.lastModified() > now) { |
| log("Warning: file modified in the future: " + |
| files[i], Project.MSG_WARN); |
| } |
| |
| if (!classFile.exists() || srcFile.lastModified() > classFile.lastModified()) { |
| //log( "Found a file newer than the instrumentDir class file: " + srcFile.getPath() + " newer than " + classFile.getPath() + ". Running iContract again..." ); |
| dirty = true; |
| } |
| } |
| } |
| if( targetPrinter != null ) { |
| targetPrinter.flush(); |
| targetPrinter.close(); |
| } |
| } catch( IOException e ) { |
| throw new BuildException( "Could not create target file:" + e.getMessage() ); |
| } |
| |
| // also, check controlFile timestamp |
| long controlFileTime = -1; |
| try { |
| if( controlFile != null ) { |
| if( controlFile.exists() && buildDir.exists() ) { |
| controlFileTime = controlFile.lastModified(); |
| ds = getDirectoryScanner( buildDir ); |
| files = ds.getIncludedFiles(); |
| for( int i = 0; i < files.length; i++ ) { |
| File srcFile = new File( srcDir, files[i] ); |
| if( files[i].endsWith( ".class" ) ) { |
| if( controlFileTime > srcFile.lastModified() ) { |
| if( !dirty ) { |
| log( "Control file " + controlFile.getAbsolutePath() + " has been updated. Instrumenting all files..." ); |
| } |
| dirty = true; |
| instrumentall = true; |
| } |
| } |
| } |
| } |
| } |
| } catch( Throwable t ) { |
| throw new BuildException( "Got an interesting exception:" + t.getMessage() ); |
| } |
| } |
| |
| |
| /** |
| * Creates the -m option based on the values of controlFile, pre, post and invariant. |
| */ |
| private final String directiveString() { |
| StringBuffer sb = new StringBuffer(); |
| boolean comma = false; |
| |
| boolean useControlFile = (controlFile != null) && controlFile.exists(); |
| if( useControlFile || pre || post || invariant ) { |
| sb.append( "-m" ); |
| } |
| if( useControlFile ) { |
| sb.append( "@" ).append( controlFile ); |
| comma = true; |
| } |
| if( pre ) { |
| if( comma ) { |
| sb.append( "," ); |
| } |
| sb.append( "pre" ); |
| comma = true; |
| } |
| if( post ) { |
| if( comma ) { |
| sb.append( "," ); |
| } |
| sb.append( "post" ); |
| comma = true; |
| } |
| if( invariant ) { |
| if( comma ) { |
| sb.append( "," ); |
| } |
| sb.append( "inv" ); |
| } |
| sb.append( " " ); |
| return sb.toString(); |
| } |
| |
| /** |
| * BuildListener that sets the iContractMissing flag to true if a |
| * message about missing iContract is missing. Used to indicate |
| * a more verbose error to the user, with advice about how to solve |
| * the problem |
| */ |
| private class IContractPresenceDetector implements BuildListener { |
| public void buildFinished(BuildEvent event) {} |
| public void buildStarted(BuildEvent event) {} |
| public void messageLogged(BuildEvent event) { |
| if( "java.lang.NoClassDefFoundError: com/reliablesystems/iContract/Tool".equals( event.getMessage() ) ) { |
| iContractMissing = true; |
| } |
| } |
| public void targetFinished(BuildEvent event) {} |
| public void targetStarted(BuildEvent event) {} |
| public void taskFinished(BuildEvent event) {} |
| public void taskStarted(BuildEvent event) {} |
| } |
| |
| /** |
| * This class is a helper to set correct classpath for other compilers, like Jikes. |
| * It reuses the logic from DefaultCompilerAdapter, which is protected, so we have |
| * to subclass it. |
| */ |
| private class ClasspathHelper extends DefaultCompilerAdapter { |
| private final String compiler; |
| public ClasspathHelper( String compiler ) { |
| super(); |
| this.compiler = compiler; |
| } |
| |
| // make it public |
| public void modify( Path path ) { |
| // depending on what compiler to use, set the includeJavaRuntime flag |
| if( "jikes".equals( compiler ) ) { |
| icCompiler = compiler; |
| includeJavaRuntime = true; |
| path.append( getCompileClasspath() ); |
| } |
| } |
| |
| // dummy implementation. Never called |
| public void setJavac( Javac javac ) {} |
| public boolean execute() { return true; } |
| } |
| } |