Browse Source

Added IContract task

Submitted By: <aslak.hellesoy@netcom.no>


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268717 13f79535-47bb-0310-9956-ffa450edef68
master
Peter Donald 24 years ago
parent
commit
16db28abfc
1 changed files with 628 additions and 0 deletions
  1. +628
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/IContract.java

+ 628
- 0
src/main/org/apache/tools/ant/taskdefs/optional/IContract.java View File

@@ -0,0 +1,628 @@
/*
* 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.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Date;
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.Task;
import org.apache.tools.ant.taskdefs.Java;
import org.apache.tools.ant.taskdefs.Mkdir;
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.
*
* @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 java and class 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 java and class files should
* go</td>
* <td valign="top" align="center">Yes</td>
* </tr>
* <tr>
* <td valign="top">pre</td>
* <td valign="top">Indicates whether or not to instrument for preconditions.
* Defaults to <code>true</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></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></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">controlfile</td>
* <td valign="top">The name of the control file to pass to iContract. Default
* is to not pass a file</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 error*,warning*,note*,info*,progress*,debug* (comma separated) can be
* used. Defaults to <code>error*,warning*</code></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 for the Javac task for
* more information.
*
* <p><b>Example:</b></p>
*
* <pre>
* &lt;!-- =================================================================== -->
* &lt;!-- Instruments source codes with iContract -->
* &lt;!-- =================================================================== -->
* &lt;target name="instrument" depends="compile">
* &lt;icontract
* srcdir="${build.src}"
* instrumentdir="${instrumented.dir}"
* repositorydir="${repository.dir}"
* >
* &lt;classpath>
* &lt;fileset dir="./lib">
* &lt;include name="*.jar"/>
* &lt;/fileset>
* &lt;/classpath>
* &lt;/icontract>
* &lt;/target>
* </pre>
*
*/
public class IContract extends Task {

/** \ 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 root */
private File instrumentDir = null;

/** repository root */
private File repositoryDir = 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*,warning*";

/** 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;

/** Indicates whether or not to instrument for postconditions */
private boolean post = true;

/** Indicates whether or not to instrument for invariants */
private boolean invariant = true;

/** 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 = true;

/**
* Sets the source directory
*
* @param srcDir the source directory
*/
public void setSrcdir( File srcDir ) {
this.srcDir = srcDir;
}

/**
* Sets the instrumentation directory
*
* @param instrumentDir the source directory
*/
public void setInstrumentdir( File instrumentDir ) {
this.instrumentDir = instrumentDir;
}

/**
* Sets the repository directory
*
* @param repositoryDir the source directory
*/
public void setRepositorydir( File repositoryDir ) {
this.repositoryDir = repositoryDir;
}

/**
* Turns on/off precondition instrumentation
*
* @param pre true turns it on
*/
public void setPre( boolean pre ) {
this.pre = pre;
}

/**
* Turns on/off postcondition instrumentation
*
* @param post true turns it on
*/
public void setPost( boolean post ) {
this.post = post;
}

/**
* Turns on/off invariant instrumentation
*
* @param invariant true turns it on
*/
public void setInvariant( boolean invariant ) {
this.invariant = invariant;
}

/**
* Sets the Throwable (Exception) to be thrown on assertion violation
*
* @param clazz the Throwable class
*/
public void setFailthrowable( Class clazz ) {
this.failThrowable = clazz.getName();
}

/**
* 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 clazz the Throwable class
*/
public void setVerbosity( String verbosity ) {
this.verbosity = verbosity;
}

/**
* Turns on/off internal compilation.
* <br/>
* If set to true, Sun's javac will be run within the same VM as Ant.
* <br/>
* If set to false, the compiler indicated by the project property
* <code>build.compiler</code> will be used, defaulting to javac,
* and run in a separate VM.
*
* @param internalcompilation set to true for internal compilation
*/
/* FIXME: Doesn't work
public void setInternalcompilation( boolean internalcompilation ) {
this.internalcompilation = internalcompilation;
}
*/

/**
* Sets the control file to pass to iContract.
*
* @param clazz the Throwable class
*/
public void setControlfile( File controlFile ) {
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 );
}

/**
* Executes the task
*
* @exception BuildException if the instrumentation fails
*/
public void execute() throws BuildException {
preconditions();
scan();
if( dirty ) {
// 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( repositoryDir );
mkdir.execute();

// Set the compiler
setCompiler();

// Set the classpath that is needed for regular Javac compilation
Path baseClasspath = createClasspath();

// 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() ) );

// 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() ) );

// 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() ) );

// 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( icCompiler ).append( "\"" ).append( " -classpath " ).append( beforeInstrumentationClasspath ).append( "\" " );
args.append( "-c" ).append( icCompiler ).append( "\"" ).append( " -classpath " ).append( afterInstrumentationClasspath ).append( "\" " );
args.append( "-n" ).append( icCompiler ).append( "\"" ).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( instrumentall ? "-a " : "" ); // reinstrument everything if controlFile exists and is newer than source
args.append( "@" ).append( targets.getName() );
iContract.createArg().setLine( args.toString() );

// System.out.println( "JAVA -classpath " + iContractClasspath + " com.reliablesystems.iContract.Tool " + args.toString() );

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 {
//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 );
}
}

/**
* 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 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();

FileSet fileset = new FileSet();
fileset.setDefaultexcludes( true );
fileset.setDir( srcDir );
DirectoryScanner ds = fileset.getDirectoryScanner( project );

String[] files = ds.getIncludedFiles();

try {
targets = File.createTempFile( "iContractTargets", "tmp", new File( System.getProperty( "user.dir" ) ) );
targets.deleteOnExit();
FileOutputStream fos = new FileOutputStream( targets );
PrintStream ps = new PrintStream( fos );
for (int i = 0; i < files.length; i++ ) {
File srcFile = new File(srcDir, files[i]);
if (files[i].endsWith(".java")) {
ps.println( srcFile.getAbsolutePath() );

File classFile = new File( instrumentDir, 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;
}
}
}
ps.flush();
ps.close();
} catch( IOException e ) {
throw new BuildException( "Could not create temporary file:" + e.getMessage() );
}

// also, check controlFile timestamp
long controlFileTime = -1;
if( controlFile != null ) {
if( controlFile.exists() ) {
controlFileTime = controlFile.lastModified();
fileset.setDir( instrumentDir );
ds = fileset.getDirectoryScanner( project );
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;
}
}
}
}
}
}


/**
* 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;
if( (controlFile != null) || pre || post || invariant ) {
sb.append( "-m" );
}
if(controlFile != null) {
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" );
comma = true;
}
sb.append( " " );
return sb.toString();
}

/**
* Sets the compiler as specified by the project's build.compiler property
* If the internalcompilation attribute is set to true, Sun's javac
* will be run from the same VM as Ant.
*
* NOTE: This has not been tested, as I only have JDK.
*/
private void setCompiler() {
if( !internalcompilation ) {
String compiler = project.getProperty("build.compiler");
if (compiler == null) {
if (Project.getJavaVersion().startsWith("1.3")) {
compiler = "modern";
} else {
compiler = "classic";
}
}

if (compiler.equalsIgnoreCase("classic")) {
icCompiler = "javac";
} else if (compiler.equalsIgnoreCase("modern")) {
icCompiler = "javac";
} else if (compiler.equalsIgnoreCase("jikes")) {
icCompiler = "jikes";
} else if (compiler.equalsIgnoreCase("jvc")) {
icCompiler = "jvc";
} else {
String msg = "Don't know how to use compiler " + compiler;
throw new BuildException(msg, location);
}
} else {
// This is how we tell iContract to use internal compiler
// FIXME: Doesn't work
// icCompiler = ":";
}
}

/**
* 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) {}
}
}

Loading…
Cancel
Save