|
|
@@ -1,939 +0,0 @@ |
|
|
|
/* |
|
|
|
* Copyright 2001-2005 The Apache Software Foundation |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* You may obtain a copy of the License at |
|
|
|
* |
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
|
|
* |
|
|
|
* Unless required by applicable law or agreed to in writing, software |
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
|
|
* See the License for the specific language governing permissions and |
|
|
|
* limitations under the License. |
|
|
|
* |
|
|
|
*/ |
|
|
|
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.Path; |
|
|
|
import org.apache.tools.ant.types.Reference; |
|
|
|
|
|
|
|
/** |
|
|
|
* Instruments Java classes with iContract 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. |
|
|
|
* iContract is at |
|
|
|
* <a href="http://www.reliable-systems.com/tools/"> |
|
|
|
* http://www.reliable-systems.com/tools/</a> |
|
|
|
* <p/> |
|
|
|
* Thanks to Rainer Schmitz for enhancements and comments. |
|
|
|
* |
|
|
|
* |
|
|
|
* <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">repbuilddir</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."; |
|
|
|
|
|
|
|
/** 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 source 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; |
|
|
|
|
|
|
|
/** 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 classDir 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 repBuildDir 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. |
|
|
|
* |
|
|
|
* @param path the classpath |
|
|
|
*/ |
|
|
|
public void setClasspath(Path path) { |
|
|
|
createClasspath().append(path); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Sets the classpath. |
|
|
|
* |
|
|
|
* @return the nested classpath element |
|
|
|
* @todo this overwrites the classpath so only one |
|
|
|
* effective classpath element would work. This |
|
|
|
* is not how we do this elsewhere. |
|
|
|
*/ |
|
|
|
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); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* If true, updates 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 = new Mkdir(); |
|
|
|
mkdir.bindToOwner(this); |
|
|
|
|
|
|
|
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 = getProject().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 = new Java(this); |
|
|
|
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); |
|
|
|
args.append(" -classpath ").append(beforeInstrumentationClasspath); |
|
|
|
args.append("\" "); |
|
|
|
|
|
|
|
args.append("-c").append("\"").append(icCompiler); |
|
|
|
args.append(" -classpath ").append(afterInstrumentationClasspath); |
|
|
|
args.append(" -d ").append(buildDir).append("\" "); |
|
|
|
|
|
|
|
args.append("-n").append("\"").append(icCompiler); |
|
|
|
args.append(" -classpath ").append(repositoryClasspath); |
|
|
|
args.append("\" "); |
|
|
|
|
|
|
|
args.append("-d").append(failThrowable).append(" "); |
|
|
|
|
|
|
|
args.append("-o").append(instrumentDir).append(File.separator); |
|
|
|
args.append("@p").append(File.separator).append("@f.@e "); |
|
|
|
|
|
|
|
args.append("-k").append(repositoryDir).append(File.separator); |
|
|
|
args.append("@p "); |
|
|
|
|
|
|
|
args.append(quiet ? "-q " : ""); |
|
|
|
// reinstrument everything if controlFile exists and is newer |
|
|
|
// than any class |
|
|
|
args.append(instrumentall ? "-a " : ""); |
|
|
|
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!", |
|
|
|
getLocation()); |
|
|
|
} |
|
|
|
if (!srcDir.exists()) { |
|
|
|
throw new BuildException("srcdir \"" + srcDir.getPath() |
|
|
|
+ "\" does not exist!", getLocation()); |
|
|
|
} |
|
|
|
if (instrumentDir == null) { |
|
|
|
throw new BuildException("instrumentdir attribute must be set!", |
|
|
|
getLocation()); |
|
|
|
} |
|
|
|
if (repositoryDir == null) { |
|
|
|
throw new BuildException("repositorydir attribute must be set!", |
|
|
|
getLocation()); |
|
|
|
} |
|
|
|
if (updateIcontrol && classDir == null) { |
|
|
|
throw new BuildException("classdir attribute must be specified " |
|
|
|
+ "when updateicontrol=true!", getLocation()); |
|
|
|
} |
|
|
|
if (updateIcontrol && controlFile == null) { |
|
|
|
throw new BuildException("controlfile attribute must be specified " |
|
|
|
+ "when updateicontrol=true!", getLocation()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* 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. |
|
|
|
* @return the string containing the -m option. |
|
|
|
*/ |
|
|
|
private 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; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|