1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.taskdefs.optional;
  18. import java.io.File;
  19. import java.io.FileInputStream;
  20. import java.io.FileOutputStream;
  21. import java.io.IOException;
  22. import java.io.PrintStream;
  23. import java.util.Date;
  24. import java.util.Properties;
  25. import org.apache.tools.ant.BuildEvent;
  26. import org.apache.tools.ant.BuildException;
  27. import org.apache.tools.ant.BuildListener;
  28. import org.apache.tools.ant.DirectoryScanner;
  29. import org.apache.tools.ant.Project;
  30. import org.apache.tools.ant.taskdefs.Java;
  31. import org.apache.tools.ant.taskdefs.Javac;
  32. import org.apache.tools.ant.taskdefs.MatchingTask;
  33. import org.apache.tools.ant.taskdefs.Mkdir;
  34. import org.apache.tools.ant.taskdefs.compilers.DefaultCompilerAdapter;
  35. import org.apache.tools.ant.types.Path;
  36. import org.apache.tools.ant.types.Reference;
  37. /**
  38. * Instruments Java classes with iContract DBC preprocessor.
  39. * <br/>
  40. * The task can generate a properties file for
  41. * <a href="http://hjem.sol.no/hellesoy/icontrol.html">iControl</a>,
  42. * a graphical user interface that lets you turn on/off assertions.
  43. * iControl generates a control file that you can refer to
  44. * from this task using the controlfile attribute.
  45. * iContract is at
  46. * <a href="http://www.reliable-systems.com/tools/">
  47. * http://www.reliable-systems.com/tools/</a>
  48. * <p/>
  49. * Thanks to Rainer Schmitz for enhancements and comments.
  50. *
  51. *
  52. * <p/>
  53. * <table border="1" cellpadding="2" cellspacing="0">
  54. * <tr>
  55. * <td valign="top"><b>Attribute</b></td>
  56. * <td valign="top"><b>Description</b></td>
  57. * <td align="center" valign="top"><b>Required</b></td>
  58. * </tr>
  59. * <tr>
  60. * <td valign="top">srcdir</td>
  61. * <td valign="top">Location of the java files.</td>
  62. * <td valign="top" align="center">Yes</td>
  63. * </tr>
  64. * <tr>
  65. * <td valign="top">instrumentdir</td>
  66. * <td valign="top">Indicates where the instrumented source
  67. * files should go.</td>
  68. * <td valign="top" align="center">Yes</td>
  69. * </tr>
  70. * <tr>
  71. * <td valign="top">repositorydir</td>
  72. * <td valign="top">Indicates where the repository source
  73. * files should go.</td>
  74. * <td valign="top" align="center">Yes</td>
  75. * </tr>
  76. * <tr>
  77. * <td valign="top">builddir</td>
  78. * <td valign="top">Indicates where the compiled instrumented
  79. * classes should go. Defaults to the value of
  80. * instrumentdir.
  81. * </p>
  82. * <em>NOTE:</em> Don't use the same directory for compiled
  83. * instrumented classes and uninstrumented classes. It will break the
  84. * dependency checking. (Classes will not be reinstrumented if you
  85. * change them).</td>
  86. * <td valign="top" align="center">No</td>
  87. * </tr>
  88. * <tr>
  89. * <td valign="top">repbuilddir</td>
  90. * <td valign="top">Indicates where the compiled repository classes
  91. * should go. Defaults to the value of repositorydir.</td>
  92. * <td valign="top" align="center">No</td>
  93. * </tr>
  94. * <tr>
  95. * <td valign="top">pre</td>
  96. * <td valign="top">Indicates whether or not to instrument for
  97. * preconditions. Defaults to <code>true</code> unless
  98. * controlfile is specified, in which case it defaults
  99. * to <code>false</code>.</td>
  100. * <td valign="top" align="center">No</td>
  101. * </tr>
  102. * <tr>
  103. * <td valign="top">post</td>
  104. * <td valign="top">Indicates whether or not to instrument for
  105. * postconditions. Defaults to <code>true</code> unless
  106. * controlfile is specified, in which case it defaults
  107. * to <code>false</code>.</td>
  108. * <td valign="top" align="center">No</td>
  109. * </tr>
  110. * <tr>
  111. * <td valign="top">invariant</td>
  112. * <td valign="top">Indicates whether or not to instrument for invariants.
  113. * Defaults to <code>true</code> unless controlfile is
  114. * specified, in which case it defaults to
  115. * <code>false</code>.</td>
  116. * <td valign="top" align="center">No</td>
  117. * </tr>
  118. * <tr>
  119. * <td valign="top">failthrowable</td>
  120. * <td valign="top">The full name of the Throwable (Exception) that
  121. * should be thrown when an assertion is violated.
  122. * Defaults to <code>java.lang.Error</code></td>
  123. * <td valign="top" align="center">No</td>
  124. * </tr>
  125. * <tr>
  126. * <td valign="top">verbosity</td>
  127. * <td valign="top">Indicates the verbosity level of iContract.
  128. * Any combination of
  129. * <code>error*,warning*,note*,info*,progress*,debug*</code>
  130. * (comma separated) can be used. Defaults to <code>error*</code></td>
  131. * <td valign="top" align="center">No</td>
  132. * </tr>
  133. * <tr>
  134. * <td valign="top">quiet</td>
  135. * <td valign="top">Indicates if iContract should be quiet. Turn it off
  136. * if many your classes extend uninstrumented classes and you don't
  137. * want warnings about this. Defaults to <code>false</code></td>
  138. * <td valign="top" align="center">No</td>
  139. * </tr>
  140. * <tr>
  141. * <td valign="top">updateicontrol</td>
  142. * <td valign="top">If set to true, it indicates that the properties
  143. * file for iControl in the current directory should be updated
  144. * (or created if it doesn't exist). Defaults to <code>false</code>.
  145. * </td>
  146. * <td valign="top" align="center">No</td>
  147. * </tr>
  148. * <tr>
  149. * <td valign="top">controlfile</td>
  150. * <td valign="top">The name of the control file to pass to iContract.
  151. * Consider using iControl to generate the file.
  152. * Default is not to pass a file. </td>
  153. * <td valign="top" align="center">
  154. * Only if <code>updateicontrol=true</code></td>
  155. * </tr>
  156. * <tr>
  157. * <td valign="top">classdir</td>
  158. * <td valign="top">Indicates where compiled (unistrumented) classes are
  159. * located. This is required in order to properly update
  160. * the icontrol.properties file, not for instrumentation.
  161. * </td>
  162. * <td valign="top" align="center">Only if
  163. * <code>updateicontrol=true</code></td>
  164. * </tr>
  165. * <tr>
  166. * <td valign="top">targets</td>
  167. * <td valign="top">Name of the file that will be generated by this task,
  168. * which lists all the classes that iContract will
  169. * instrument. If specified, the file will not be deleted
  170. * after execution. If not specified, a file will still
  171. * be created, but it will be deleted after execution.</td>
  172. * <td valign="top" align="center">No</td>
  173. * </tr>
  174. * </table>
  175. *
  176. * <p/>
  177. * <b>Note:</b> iContract will use the java compiler indicated by the project's
  178. * <code>build.compiler</code> property. See documentation of the Javac task for
  179. * more information.
  180. * <p/>
  181. * Nested includes and excludes are also supported.
  182. *
  183. * <p><b>Example:</b></p>
  184. * <pre>
  185. * <icontract
  186. * srcdir="${build.src}"
  187. * instrumentdir="${build.instrument}"
  188. * repositorydir="${build.repository}"
  189. * builddir="${build.instrclasses}"
  190. * updateicontrol="true"
  191. * classdir="${build.classes}"
  192. * controlfile="control"
  193. * targets="targets"
  194. * verbosity="error*,warning*"
  195. * quiet="true"
  196. * >
  197. * <classpath refid="compile-classpath"/>
  198. * </icontract>
  199. * </pre>
  200. *
  201. */
  202. public class IContract extends MatchingTask {
  203. private static final String ICONTROL_PROPERTIES_HEADER
  204. = "You might want to set classRoot to point to your normal "
  205. + "compilation class root directory.";
  206. /** compiler to use for instrumenation */
  207. private String icCompiler = "javac";
  208. /** temporary file with file names of all java files to be instrumented */
  209. private File targets = null;
  210. /**
  211. * will be set to true if any of the source files are newer than the
  212. * instrumented files
  213. */
  214. private boolean dirty = false;
  215. /** set to true if the iContract jar is missing */
  216. private boolean iContractMissing = false;
  217. /** source file root */
  218. private File srcDir = null;
  219. /** instrumentation src root */
  220. private File instrumentDir = null;
  221. /** instrumentation build root */
  222. private File buildDir = null;
  223. /** repository src root */
  224. private File repositoryDir = null;
  225. /** repository build root */
  226. private File repBuildDir = null;
  227. /** classpath */
  228. private Path classpath = null;
  229. /** The class of the Throwable to be thrown on failed assertions */
  230. private String failThrowable = "java.lang.Error";
  231. /** The -v option */
  232. private String verbosity = "error*";
  233. /** The -q option */
  234. private boolean quiet = false;
  235. /** The -m option */
  236. private File controlFile = null;
  237. /** Indicates whether or not to instrument for preconditions */
  238. private boolean pre = true;
  239. private boolean preModified = false;
  240. /** Indicates whether or not to instrument for postconditions */
  241. private boolean post = true;
  242. private boolean postModified = false;
  243. /** Indicates whether or not to instrument for invariants */
  244. private boolean invariant = true;
  245. private boolean invariantModified = false;
  246. /**
  247. * Indicates whether or not to instrument all files regardless of timestamp
  248. *
  249. * Can't be explicitly set, is set if control file exists and is newer
  250. * than any source file
  251. */
  252. private boolean instrumentall = false;
  253. /**
  254. * Indicates the name of a properties file (intentionally for iControl)
  255. * where the classpath property should be updated.
  256. */
  257. private boolean updateIcontrol = false;
  258. /** Regular compilation class root */
  259. private File classDir = null;
  260. /**
  261. * Sets the source directory.
  262. *
  263. * @param srcDir the source directory
  264. */
  265. public void setSrcdir(File srcDir) {
  266. this.srcDir = srcDir;
  267. }
  268. /**
  269. * Sets the class directory (uninstrumented classes).
  270. *
  271. * @param classDir the source directory
  272. */
  273. public void setClassdir(File classDir) {
  274. this.classDir = classDir;
  275. }
  276. /**
  277. * Sets the instrumentation directory.
  278. *
  279. * @param instrumentDir the source directory
  280. */
  281. public void setInstrumentdir(File instrumentDir) {
  282. this.instrumentDir = instrumentDir;
  283. if (this.buildDir == null) {
  284. setBuilddir(instrumentDir);
  285. }
  286. }
  287. /**
  288. * Sets the build directory for instrumented classes.
  289. *
  290. * @param buildDir the build directory
  291. */
  292. public void setBuilddir(File buildDir) {
  293. this.buildDir = buildDir;
  294. }
  295. /**
  296. * Sets the build directory for repository classes.
  297. *
  298. * @param repositoryDir the source directory
  299. */
  300. public void setRepositorydir(File repositoryDir) {
  301. this.repositoryDir = repositoryDir;
  302. if (this.repBuildDir == null) {
  303. setRepbuilddir(repositoryDir);
  304. }
  305. }
  306. /**
  307. * Sets the build directory for instrumented classes.
  308. *
  309. * @param repBuildDir the build directory
  310. */
  311. public void setRepbuilddir(File repBuildDir) {
  312. this.repBuildDir = repBuildDir;
  313. }
  314. /**
  315. * Turns on/off precondition instrumentation.
  316. *
  317. * @param pre true turns it on
  318. */
  319. public void setPre(boolean pre) {
  320. this.pre = pre;
  321. preModified = true;
  322. }
  323. /**
  324. * Turns on/off postcondition instrumentation.
  325. *
  326. * @param post true turns it on
  327. */
  328. public void setPost(boolean post) {
  329. this.post = post;
  330. postModified = true;
  331. }
  332. /**
  333. * Turns on/off invariant instrumentation.
  334. *
  335. * @param invariant true turns it on
  336. */
  337. public void setInvariant(boolean invariant) {
  338. this.invariant = invariant;
  339. invariantModified = true;
  340. }
  341. /**
  342. * Sets the Throwable (Exception) to be thrown on assertion violation.
  343. *
  344. * @param clazz the fully qualified Throwable class name
  345. */
  346. public void setFailthrowable(String clazz) {
  347. this.failThrowable = clazz;
  348. }
  349. /**
  350. * Sets the verbosity level of iContract. Any combination of
  351. * error*,warning*,note*,info*,progress*,debug* (comma separated) can be
  352. * used. Defaults to error*,warning*
  353. *
  354. * @param verbosity verbosity level
  355. */
  356. public void setVerbosity(String verbosity) {
  357. this.verbosity = verbosity;
  358. }
  359. /**
  360. * Tells iContract to be quiet.
  361. *
  362. * @param quiet true if iContract should be quiet.
  363. */
  364. public void setQuiet(boolean quiet) {
  365. this.quiet = quiet;
  366. }
  367. /**
  368. * Sets the name of the file where targets will be written. That is the
  369. * file that tells iContract what files to process.
  370. *
  371. * @param targets the targets file name
  372. */
  373. public void setTargets(File targets) {
  374. this.targets = targets;
  375. }
  376. /**
  377. * Sets the control file to pass to iContract.
  378. *
  379. * @param controlFile the control file
  380. */
  381. public void setControlfile(File controlFile) {
  382. if (!controlFile.exists()) {
  383. log("WARNING: Control file " + controlFile.getAbsolutePath()
  384. + " doesn't exist. iContract will be run "
  385. + "without control file.");
  386. }
  387. this.controlFile = controlFile;
  388. }
  389. /**
  390. * Sets the classpath to be used for invocation of iContract.
  391. *
  392. * @param path the classpath
  393. */
  394. public void setClasspath(Path path) {
  395. createClasspath().append(path);
  396. }
  397. /**
  398. * Sets the classpath.
  399. *
  400. * @return the nested classpath element
  401. * @todo this overwrites the classpath so only one
  402. * effective classpath element would work. This
  403. * is not how we do this elsewhere.
  404. */
  405. public Path createClasspath() {
  406. if (classpath == null) {
  407. classpath = new Path(getProject());
  408. }
  409. return classpath;
  410. }
  411. /**
  412. * Adds a reference to a classpath defined elsewhere.
  413. *
  414. * @param reference referenced classpath
  415. */
  416. public void setClasspathRef(Reference reference) {
  417. createClasspath().setRefid(reference);
  418. }
  419. /**
  420. * If true, updates iControl properties file
  421. *
  422. * @param updateIcontrol true if iControl properties file should be
  423. * updated
  424. */
  425. public void setUpdateicontrol(boolean updateIcontrol) {
  426. this.updateIcontrol = updateIcontrol;
  427. }
  428. /**
  429. * Executes the task
  430. *
  431. * @exception BuildException if the instrumentation fails
  432. */
  433. public void execute() throws BuildException {
  434. preconditions();
  435. scan();
  436. if (dirty) {
  437. // turn off assertions if we're using controlfile, unless they are not explicitly set.
  438. boolean useControlFile = (controlFile != null) && controlFile.exists();
  439. if (useControlFile && !preModified) {
  440. pre = false;
  441. }
  442. if (useControlFile && !postModified) {
  443. post = false;
  444. }
  445. if (useControlFile && !invariantModified) {
  446. invariant = false;
  447. }
  448. // issue warning if pre,post or invariant is used together with controlfile
  449. if ((pre || post || invariant) && controlFile != null) {
  450. log("WARNING: specifying pre,post or invariant will "
  451. + "override control file settings");
  452. }
  453. // We want to be notified if iContract jar is missing.
  454. // This makes life easier for the user who didn't understand
  455. // that iContract is a separate library (duh!)
  456. getProject().addBuildListener(new IContractPresenceDetector());
  457. // Prepare the directories for iContract. iContract will make
  458. // them if they don't exist, but for some reason I don't know,
  459. // it will complain about the REP files afterwards
  460. Mkdir mkdir = (Mkdir) getProject().createTask("mkdir");
  461. mkdir.setDir(instrumentDir);
  462. mkdir.execute();
  463. mkdir.setDir(buildDir);
  464. mkdir.execute();
  465. mkdir.setDir(repositoryDir);
  466. mkdir.execute();
  467. // Set the classpath that is needed for regular Javac compilation
  468. Path baseClasspath = createClasspath();
  469. // Might need to add the core classes if we're not using
  470. // Sun's Javac (like Jikes)
  471. String compiler = getProject().getProperty("build.compiler");
  472. ClasspathHelper classpathHelper = new ClasspathHelper(compiler);
  473. classpathHelper.modify(baseClasspath);
  474. // Create the classpath required to compile the sourcefiles
  475. // BEFORE instrumentation
  476. Path beforeInstrumentationClasspath = ((Path) baseClasspath.clone());
  477. beforeInstrumentationClasspath.append(new Path(getProject(),
  478. srcDir.getAbsolutePath()));
  479. // Create the classpath required to compile the sourcefiles
  480. // AFTER instrumentation
  481. Path afterInstrumentationClasspath = ((Path) baseClasspath.clone());
  482. afterInstrumentationClasspath.append(new Path(getProject(),
  483. instrumentDir.getAbsolutePath()));
  484. afterInstrumentationClasspath.append(new Path(getProject(),
  485. repositoryDir.getAbsolutePath()));
  486. afterInstrumentationClasspath.append(new Path(getProject(),
  487. srcDir.getAbsolutePath()));
  488. afterInstrumentationClasspath.append(new Path(getProject(),
  489. buildDir.getAbsolutePath()));
  490. // Create the classpath required to automatically compile the
  491. // repository files
  492. Path repositoryClasspath = ((Path) baseClasspath.clone());
  493. repositoryClasspath.append(new Path(getProject(),
  494. instrumentDir.getAbsolutePath()));
  495. repositoryClasspath.append(new Path(getProject(),
  496. srcDir.getAbsolutePath()));
  497. repositoryClasspath.append(new Path(getProject(),
  498. repositoryDir.getAbsolutePath()));
  499. repositoryClasspath.append(new Path(getProject(),
  500. buildDir.getAbsolutePath()));
  501. // Create the classpath required for iContract itself
  502. Path iContractClasspath = ((Path) baseClasspath.clone());
  503. iContractClasspath.append(new Path(getProject(),
  504. System.getProperty("java.home") + File.separator + ".."
  505. + File.separator + "lib" + File.separator + "tools.jar"));
  506. iContractClasspath.append(new Path(getProject(),
  507. srcDir.getAbsolutePath()));
  508. iContractClasspath.append(new Path(getProject(),
  509. repositoryDir.getAbsolutePath()));
  510. iContractClasspath.append(new Path(getProject(),
  511. instrumentDir.getAbsolutePath()));
  512. iContractClasspath.append(new Path(getProject(),
  513. buildDir.getAbsolutePath()));
  514. // Create a forked java process
  515. Java iContract = (Java) getProject().createTask("java");
  516. iContract.setTaskName(getTaskName());
  517. iContract.setFork(true);
  518. iContract.setClassname("com.reliablesystems.iContract.Tool");
  519. iContract.setClasspath(iContractClasspath);
  520. // Build the arguments to iContract
  521. StringBuffer args = new StringBuffer();
  522. args.append(directiveString());
  523. args.append("-v").append(verbosity).append(" ");
  524. args.append("-b").append("\"").append(icCompiler);
  525. args.append(" -classpath ").append(beforeInstrumentationClasspath);
  526. args.append("\" ");
  527. args.append("-c").append("\"").append(icCompiler);
  528. args.append(" -classpath ").append(afterInstrumentationClasspath);
  529. args.append(" -d ").append(buildDir).append("\" ");
  530. args.append("-n").append("\"").append(icCompiler);
  531. args.append(" -classpath ").append(repositoryClasspath);
  532. args.append("\" ");
  533. args.append("-d").append(failThrowable).append(" ");
  534. args.append("-o").append(instrumentDir).append(File.separator);
  535. args.append("@p").append(File.separator).append("@f.@e ");
  536. args.append("-k").append(repositoryDir).append(File.separator);
  537. args.append("@p ");
  538. args.append(quiet ? "-q " : "");
  539. // reinstrument everything if controlFile exists and is newer
  540. // than any class
  541. args.append(instrumentall ? "-a " : "");
  542. args.append("@").append(targets.getAbsolutePath());
  543. iContract.createArg().setLine(args.toString());
  544. //System.out.println( "JAVA -classpath " + iContractClasspath
  545. // + " com.reliablesystems.iContract.Tool " + args.toString() );
  546. // update iControlProperties if it's set.
  547. if (updateIcontrol) {
  548. Properties iControlProps = new Properties();
  549. try {
  550. // to read existing propertiesfile
  551. iControlProps.load(new FileInputStream("icontrol.properties"));
  552. } catch (IOException e) {
  553. log("File icontrol.properties not found. That's ok. "
  554. + "Writing a default one.");
  555. }
  556. iControlProps.setProperty("sourceRoot",
  557. srcDir.getAbsolutePath());
  558. iControlProps.setProperty("classRoot",
  559. classDir.getAbsolutePath());
  560. iControlProps.setProperty("classpath",
  561. afterInstrumentationClasspath.toString());
  562. iControlProps.setProperty("controlFile",
  563. controlFile.getAbsolutePath());
  564. iControlProps.setProperty("targetsFile",
  565. targets.getAbsolutePath());
  566. try {
  567. // to read existing propertiesfile
  568. iControlProps.store(new FileOutputStream("icontrol.properties"),
  569. ICONTROL_PROPERTIES_HEADER);
  570. log("Updated icontrol.properties");
  571. } catch (IOException e) {
  572. log("Couldn't write icontrol.properties.");
  573. }
  574. }
  575. // do it!
  576. int result = iContract.executeJava();
  577. if (result != 0) {
  578. if (iContractMissing) {
  579. log("iContract can't be found on your classpath. "
  580. + "Your classpath is:");
  581. log(classpath.toString());
  582. log("If you don't have the iContract jar, go get it at "
  583. + "http://www.reliable-systems.com/tools/");
  584. }
  585. throw new BuildException("iContract instrumentation failed. "
  586. + "Code = " + result);
  587. }
  588. } else {
  589. // not dirty
  590. //log( "Nothing to do. Everything up to date." );
  591. }
  592. }
  593. /** Checks that the required attributes are set. */
  594. private void preconditions() throws BuildException {
  595. if (srcDir == null) {
  596. throw new BuildException("srcdir attribute must be set!",
  597. getLocation());
  598. }
  599. if (!srcDir.exists()) {
  600. throw new BuildException("srcdir \"" + srcDir.getPath()
  601. + "\" does not exist!", getLocation());
  602. }
  603. if (instrumentDir == null) {
  604. throw new BuildException("instrumentdir attribute must be set!",
  605. getLocation());
  606. }
  607. if (repositoryDir == null) {
  608. throw new BuildException("repositorydir attribute must be set!",
  609. getLocation());
  610. }
  611. if (updateIcontrol && classDir == null) {
  612. throw new BuildException("classdir attribute must be specified "
  613. + "when updateicontrol=true!", getLocation());
  614. }
  615. if (updateIcontrol && controlFile == null) {
  616. throw new BuildException("controlfile attribute must be specified "
  617. + "when updateicontrol=true!", getLocation());
  618. }
  619. }
  620. /**
  621. * Verifies whether any of the source files have changed. Done by
  622. * comparing date of source/class files. The whole lot is "dirty" if at
  623. * least one source file or the control file is newer than the
  624. * instrumented files. If not dirty, iContract will not be executed. <br/>
  625. * Also creates a temporary file with a list of the source files, that
  626. * will be deleted upon exit.
  627. */
  628. private void scan() throws BuildException {
  629. long now = (new Date()).getTime();
  630. DirectoryScanner ds = null;
  631. ds = getDirectoryScanner(srcDir);
  632. String[] files = ds.getIncludedFiles();
  633. FileOutputStream targetOutputStream = null;
  634. PrintStream targetPrinter = null;
  635. boolean writeTargets = false;
  636. try {
  637. if (targets == null) {
  638. targets = new File("targets");
  639. log("Warning: targets file not specified. generating file: "
  640. + targets.getName());
  641. writeTargets = true;
  642. } else if (!targets.exists()) {
  643. log("Specified targets file doesn't exist. generating file: "
  644. + targets.getName());
  645. writeTargets = true;
  646. }
  647. if (writeTargets) {
  648. log("You should consider using iControl to create a target file.");
  649. targetOutputStream = new FileOutputStream(targets);
  650. targetPrinter = new PrintStream(targetOutputStream);
  651. }
  652. for (int i = 0; i < files.length; i++) {
  653. File srcFile = new File(srcDir, files[i]);
  654. if (files[i].endsWith(".java")) {
  655. // print the target, while we're at here. (Only if generatetarget=true).
  656. if (targetPrinter != null) {
  657. targetPrinter.println(srcFile.getAbsolutePath());
  658. }
  659. File classFile
  660. = new File(buildDir, files[i].substring(0, files[i].indexOf(".java")) + ".class");
  661. if (srcFile.lastModified() > now) {
  662. log("Warning: file modified in the future: "
  663. + files[i], Project.MSG_WARN);
  664. }
  665. if (!classFile.exists() || srcFile.lastModified() > classFile.lastModified()) {
  666. //log( "Found a file newer than the instrumentDir class file: "
  667. // + srcFile.getPath() + " newer than " + classFile.getPath()
  668. // + ". Running iContract again..." );
  669. dirty = true;
  670. }
  671. }
  672. }
  673. if (targetPrinter != null) {
  674. targetPrinter.flush();
  675. targetPrinter.close();
  676. }
  677. } catch (IOException e) {
  678. throw new BuildException("Could not create target file:" + e.getMessage());
  679. }
  680. // also, check controlFile timestamp
  681. long controlFileTime = -1;
  682. try {
  683. if (controlFile != null) {
  684. if (controlFile.exists() && buildDir.exists()) {
  685. controlFileTime = controlFile.lastModified();
  686. ds = getDirectoryScanner(buildDir);
  687. files = ds.getIncludedFiles();
  688. for (int i = 0; i < files.length; i++) {
  689. File srcFile = new File(srcDir, files[i]);
  690. if (files[i].endsWith(".class")) {
  691. if (controlFileTime > srcFile.lastModified()) {
  692. if (!dirty) {
  693. log("Control file "
  694. + controlFile.getAbsolutePath()
  695. + " has been updated. "
  696. + "Instrumenting all files...");
  697. }
  698. dirty = true;
  699. instrumentall = true;
  700. }
  701. }
  702. }
  703. }
  704. }
  705. } catch (Throwable t) {
  706. throw new BuildException("Got an interesting exception:"
  707. + t.getMessage());
  708. }
  709. }
  710. /**
  711. * Creates the -m option based on the values of controlFile, pre, post and
  712. * invariant.
  713. */
  714. private final String directiveString() {
  715. StringBuffer sb = new StringBuffer();
  716. boolean comma = false;
  717. boolean useControlFile = (controlFile != null) && controlFile.exists();
  718. if (useControlFile || pre || post || invariant) {
  719. sb.append("-m");
  720. }
  721. if (useControlFile) {
  722. sb.append("@").append(controlFile);
  723. comma = true;
  724. }
  725. if (pre) {
  726. if (comma) {
  727. sb.append(",");
  728. }
  729. sb.append("pre");
  730. comma = true;
  731. }
  732. if (post) {
  733. if (comma) {
  734. sb.append(",");
  735. }
  736. sb.append("post");
  737. comma = true;
  738. }
  739. if (invariant) {
  740. if (comma) {
  741. sb.append(",");
  742. }
  743. sb.append("inv");
  744. }
  745. sb.append(" ");
  746. return sb.toString();
  747. }
  748. /**
  749. * BuildListener that sets the iContractMissing flag to true if a message
  750. * about missing iContract is missing. Used to indicate a more verbose
  751. * error to the user, with advice about how to solve the problem
  752. *
  753. */
  754. private class IContractPresenceDetector implements BuildListener {
  755. public void buildFinished(BuildEvent event) {
  756. }
  757. public void buildStarted(BuildEvent event) {
  758. }
  759. public void messageLogged(BuildEvent event) {
  760. if ("java.lang.NoClassDefFoundError: com/reliablesystems/iContract/Tool".equals(event.getMessage())) {
  761. iContractMissing = true;
  762. }
  763. }
  764. public void targetFinished(BuildEvent event) {
  765. }
  766. public void targetStarted(BuildEvent event) {
  767. }
  768. public void taskFinished(BuildEvent event) {
  769. }
  770. public void taskStarted(BuildEvent event) {
  771. }
  772. }
  773. /**
  774. * This class is a helper to set correct classpath for other compilers,
  775. * like Jikes. It reuses the logic from DefaultCompilerAdapter, which is
  776. * protected, so we have to subclass it.
  777. *
  778. */
  779. private class ClasspathHelper extends DefaultCompilerAdapter {
  780. private final String compiler;
  781. public ClasspathHelper(String compiler) {
  782. super();
  783. this.compiler = compiler;
  784. }
  785. // make it public
  786. public void modify(Path path) {
  787. // depending on what compiler to use, set the
  788. // includeJavaRuntime flag
  789. if ("jikes".equals(compiler)) {
  790. icCompiler = compiler;
  791. includeJavaRuntime = true;
  792. path.append(getCompileClasspath());
  793. }
  794. }
  795. // dummy implementation. Never called
  796. public void setJavac(Javac javac) {
  797. }
  798. public boolean execute() {
  799. return true;
  800. }
  801. }
  802. }