All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.tools.ant.taskdefs.optional.IContract Maven / Gradle / Ivy

There is a newer version: 1.0-rc5
Show newest version
/*
 * Copyright  2001-2004 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.
 * 
* The task can generate a properties file for * iControl, * 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 * * http://www.reliable-systems.com/tools/ *

* Thanks to Rainer Schmitz for enhancements and comments. * * *

*

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
AttributeDescriptionRequired
srcdirLocation of the java files.Yes
instrumentdirIndicates where the instrumented source * files should go.Yes
repositorydirIndicates where the repository source * files should go.Yes
builddirIndicates where the compiled instrumented * classes should go. Defaults to the value of * instrumentdir. *

* NOTE: 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).
No
repbuilddirIndicates where the compiled repository classes * should go. Defaults to the value of repositorydir.No
preIndicates whether or not to instrument for * preconditions. Defaults to true unless * controlfile is specified, in which case it defaults * to false.No
postIndicates whether or not to instrument for * postconditions. Defaults to true unless * controlfile is specified, in which case it defaults * to false.No
invariantIndicates whether or not to instrument for invariants. * Defaults to true unless controlfile is * specified, in which case it defaults to * false.No
failthrowableThe full name of the Throwable (Exception) that * should be thrown when an assertion is violated. * Defaults to java.lang.ErrorNo
verbosityIndicates the verbosity level of iContract. * Any combination of * error*,warning*,note*,info*,progress*,debug* * (comma separated) can be used. Defaults to error*No
quietIndicates 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 falseNo
updateicontrolIf 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 false. * No
controlfileThe name of the control file to pass to iContract. * Consider using iControl to generate the file. * Default is not to pass a file. * Only if updateicontrol=true
classdirIndicates where compiled (unistrumented) classes are * located. This is required in order to properly update * the icontrol.properties file, not for instrumentation. * Only if * updateicontrol=true
targetsName 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.No
* *

* Note: iContract will use the java compiler indicated by the project's * build.compiler property. See documentation of the Javac task for * more information. *

* Nested includes and excludes are also supported. * *

Example:

*
 * <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>
 * 
* */ 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 = (Mkdir) getProject().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 = 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 = (Java) getProject().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); 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.
* 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; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy