You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

SubAnt.java 15 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. /*
  2. * Copyright 2003-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;
  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.util.Vector;
  21. import java.util.Enumeration;
  22. import org.apache.tools.ant.Task;
  23. import org.apache.tools.ant.Project;
  24. import org.apache.tools.ant.BuildException;
  25. import org.apache.tools.ant.types.Path;
  26. import org.apache.tools.ant.types.DirSet;
  27. import org.apache.tools.ant.types.FileSet;
  28. import org.apache.tools.ant.types.FileList;
  29. import org.apache.tools.ant.types.PropertySet;
  30. import org.apache.tools.ant.types.Reference;
  31. /**
  32. * Calls a given target for all defined sub-builds. This is an extension
  33. * of ant for bulk project execution.
  34. * <p>
  35. * <h2> Use with directories </h2>
  36. * <p>
  37. * subant can be used with directory sets to execute a build from different directories.
  38. * 2 different options are offered
  39. * </p>
  40. * <ul>
  41. * <li>
  42. * run the same build file /somepath/otherpath/mybuild.xml
  43. * with different base directories use the genericantfile attribute
  44. * </li>
  45. * <li>if you want to run directory1/build.xml, directory2/build.xml, ....
  46. * use the antfile attribute. The base directory does not get set by the subant task in this case,
  47. * because you can specify it in each build file.
  48. * </li>
  49. * </ul>
  50. * @since Ant1.6
  51. * @ant.task name="subant" category="control"
  52. */
  53. public class SubAnt
  54. extends Task {
  55. private Path buildpath;
  56. private String target = null;
  57. private String antfile = "build.xml";
  58. private File genericantfile = null;
  59. private boolean inheritAll = false;
  60. private boolean inheritRefs = false;
  61. private boolean failOnError = true;
  62. private String output = null;
  63. private Vector properties = new Vector();
  64. private Vector references = new Vector();
  65. private Vector propertySets = new Vector();
  66. /**
  67. * Runs the various sub-builds.
  68. */
  69. public void execute() {
  70. if (buildpath == null) {
  71. throw new BuildException("No buildpath specified");
  72. }
  73. final String[] filenames = buildpath.list();
  74. final int count = filenames.length;
  75. if (count < 1) {
  76. log("No sub-builds to iterate on", Project.MSG_WARN);
  77. return;
  78. }
  79. /*
  80. //REVISIT: there must be cleaner way of doing this, if it is merited at all
  81. if (target == null) {
  82. target = getOwningTarget().getName();
  83. }
  84. */
  85. BuildException buildException = null;
  86. for (int i = 0; i < count; ++i) {
  87. File file = null;
  88. Throwable thrownException = null;
  89. try {
  90. File directory = null;
  91. file = new File(filenames[i]);
  92. if (file.isDirectory()) {
  93. if (genericantfile != null) {
  94. directory = file;
  95. file = genericantfile;
  96. } else {
  97. file = new File(file, antfile);
  98. }
  99. }
  100. execute(file, directory);
  101. } catch (RuntimeException ex) {
  102. if (!(getProject().isKeepGoingMode())) {
  103. throw ex; // throw further
  104. }
  105. thrownException = ex;
  106. } catch (Throwable ex) {
  107. if (!(getProject().isKeepGoingMode())) {
  108. throw new BuildException(ex);
  109. }
  110. thrownException = ex;
  111. }
  112. if (thrownException != null) {
  113. if (thrownException instanceof BuildException) {
  114. log("File '" + file
  115. + "' failed with message '"
  116. + thrownException.getMessage() + "'.", Project.MSG_ERR);
  117. // only the first build exception is reported
  118. if (buildException == null) {
  119. buildException = (BuildException) thrownException;
  120. }
  121. } else {
  122. log("Target '" + file
  123. + "' failed with message '"
  124. + thrownException.getMessage() + "'.", Project.MSG_ERR);
  125. thrownException.printStackTrace(System.err);
  126. if (buildException == null) {
  127. buildException =
  128. new BuildException(thrownException);
  129. }
  130. }
  131. }
  132. }
  133. // check if one of the builds failed in keep going mode
  134. if (buildException != null) {
  135. throw buildException;
  136. }
  137. }
  138. /**
  139. * Runs the given target on the provided build file.
  140. *
  141. * @param file the build file to execute
  142. * @param directory the directory of the current iteration
  143. * @throws BuildException is the file cannot be found, read, is
  144. * a directory, or the target called failed, but only if
  145. * <code>failOnError</code> is <code>true</code>. Otherwise,
  146. * a warning log message is simply output.
  147. */
  148. private void execute(File file, File directory)
  149. throws BuildException {
  150. if (!file.exists() || file.isDirectory() || !file.canRead()) {
  151. String msg = "Invalid file: " + file;
  152. if (failOnError) {
  153. throw new BuildException(msg);
  154. }
  155. log(msg, Project.MSG_WARN);
  156. return;
  157. }
  158. Ant ant = createAntTask(directory);
  159. String antfilename = null;
  160. try {
  161. antfilename = file.getCanonicalPath();
  162. } catch (IOException e) {
  163. throw new BuildException(e);
  164. }
  165. ant.setAntfile(antfilename);
  166. try {
  167. ant.execute();
  168. } catch (BuildException e) {
  169. if (failOnError) {
  170. throw e;
  171. }
  172. log("Failure for target '" + target
  173. + "' of: " + antfilename + "\n"
  174. + e.getMessage(), Project.MSG_WARN);
  175. } catch (Throwable e) {
  176. if (failOnError) {
  177. throw new BuildException(e);
  178. }
  179. log("Failure for target '" + target
  180. + "' of: " + antfilename + "\n"
  181. + e.toString(),
  182. Project.MSG_WARN);
  183. }
  184. }
  185. /**
  186. * This method builds the file name to use in conjunction with directories.
  187. *
  188. * <p>Defaults to "build.xml".
  189. * If <code>genericantfile</code> is set, this attribute is ignored.</p>
  190. *
  191. * @param antfile the short build file name. Defaults to "build.xml".
  192. */
  193. public void setAntfile(String antfile) {
  194. this.antfile = antfile;
  195. }
  196. /**
  197. * This method builds a file path to use in conjunction with directories.
  198. *
  199. * <p>Use <code>genericantfile</code>, in order to run the same build file
  200. * with different basedirs.</p>
  201. * If this attribute is set, <code>antfile</code> is ignored.
  202. *
  203. * @param afile (path of the generic ant file, absolute or relative to
  204. * project base directory)
  205. * */
  206. public void setGenericAntfile(File afile) {
  207. this.genericantfile = afile;
  208. }
  209. /**
  210. * Sets whether to fail with a build exception on error, or go on.
  211. *
  212. * @param failOnError the new value for this boolean flag.
  213. */
  214. public void setFailonerror(boolean failOnError) {
  215. this.failOnError = failOnError;
  216. }
  217. /**
  218. * The target to call on the different sub-builds. Set to "" to execute
  219. * the default target.
  220. * @param target the target
  221. * <p>
  222. */
  223. // REVISIT: Defaults to the target name that contains this task if not specified.
  224. public void setTarget(String target) {
  225. this.target = target;
  226. }
  227. /**
  228. * Corresponds to <code>&lt;ant&gt;</code>'s
  229. * <code>output</code> attribute.
  230. *
  231. * @param s the filename to write the output to.
  232. */
  233. public void setOutput(String s) {
  234. this.output = s;
  235. }
  236. /**
  237. * Corresponds to <code>&lt;ant&gt;</code>'s
  238. * <code>inheritall</code> attribute.
  239. *
  240. * @param b the new value for this boolean flag.
  241. */
  242. public void setInheritall(boolean b) {
  243. this.inheritAll = b;
  244. }
  245. /**
  246. * Corresponds to <code>&lt;ant&gt;</code>'s
  247. * <code>inheritrefs</code> attribute.
  248. *
  249. * @param b the new value for this boolean flag.
  250. */
  251. public void setInheritrefs(boolean b) {
  252. this.inheritRefs = b;
  253. }
  254. /**
  255. * Corresponds to <code>&lt;ant&gt;</code>'s
  256. * nested <code>&lt;property&gt;</code> element.
  257. *
  258. * @param p the property to pass on explicitly to the sub-build.
  259. */
  260. public void addProperty(Property p) {
  261. properties.addElement(p);
  262. }
  263. /**
  264. * Corresponds to <code>&lt;ant&gt;</code>'s
  265. * nested <code>&lt;reference&gt;</code> element.
  266. *
  267. * @param r the reference to pass on explicitly to the sub-build.
  268. */
  269. public void addReference(Ant.Reference r) {
  270. references.addElement(r);
  271. }
  272. /**
  273. * Corresponds to <code>&lt;ant&gt;</code>'s
  274. * nested <code>&lt;propertyset&gt;</code> element.
  275. * @param ps the propertset
  276. */
  277. public void addPropertyset(PropertySet ps) {
  278. propertySets.addElement(ps);
  279. }
  280. /**
  281. * Adds a directory set to the implicit build path.
  282. * <p>
  283. * <em>Note that the directories will be added to the build path
  284. * in no particular order, so if order is significant, one should
  285. * use a file list instead!</em>
  286. *
  287. * @param set the directory set to add.
  288. */
  289. public void addDirset(DirSet set) {
  290. getBuildpath().addDirset(set);
  291. }
  292. /**
  293. * Adds a file set to the implicit build path.
  294. * <p>
  295. * <em>Note that the directories will be added to the build path
  296. * in no particular order, so if order is significant, one should
  297. * use a file list instead!</em>
  298. *
  299. * @param set the file set to add.
  300. */
  301. public void addFileset(FileSet set) {
  302. getBuildpath().addFileset(set);
  303. }
  304. /**
  305. * Adds an ordered file list to the implicit build path.
  306. * <p>
  307. * <em>Note that contrary to file and directory sets, file lists
  308. * can reference non-existent files or directories!</em>
  309. *
  310. * @param list the file list to add.
  311. */
  312. public void addFilelist(FileList list) {
  313. getBuildpath().addFilelist(list);
  314. }
  315. /**
  316. * Set the buildpath to be used to find sub-projects.
  317. *
  318. * @param s an Ant Path object containing the buildpath.
  319. */
  320. public void setBuildpath(Path s) {
  321. getBuildpath().append(s);
  322. }
  323. /**
  324. * Creates a nested build path, and add it to the implicit build path.
  325. *
  326. * @return the newly created nested build path.
  327. */
  328. public Path createBuildpath() {
  329. return getBuildpath().createPath();
  330. }
  331. /**
  332. * Creates a nested <code>&lt;buildpathelement&gt;</code>,
  333. * and add it to the implicit build path.
  334. *
  335. * @return the newly created nested build path element.
  336. */
  337. public Path.PathElement createBuildpathElement() {
  338. return getBuildpath().createPathElement();
  339. }
  340. /**
  341. * Gets the implicit build path, creating it if <code>null</code>.
  342. *
  343. * @return the implicit build path.
  344. */
  345. private Path getBuildpath() {
  346. if (buildpath == null) {
  347. buildpath = new Path(getProject());
  348. }
  349. return buildpath;
  350. }
  351. /**
  352. * Buildpath to use, by reference.
  353. *
  354. * @param r a reference to an Ant Path object containing the buildpath.
  355. */
  356. public void setBuildpathRef(Reference r) {
  357. createBuildpath().setRefid(r);
  358. }
  359. /**
  360. * Creates the &lt;ant&gt; task configured to run a specific target.
  361. *
  362. * @param directory : if not null the directory where the build should run
  363. *
  364. * @return the ant task, configured with the explicit properties and
  365. * references necessary to run the sub-build.
  366. */
  367. private Ant createAntTask(File directory) {
  368. Ant ant = (Ant) getProject().createTask("ant");
  369. ant.setOwningTarget(getOwningTarget());
  370. ant.setTaskName(getTaskName());
  371. ant.init();
  372. if (target != null && target.length() > 0) {
  373. ant.setTarget(target);
  374. }
  375. if (output != null) {
  376. ant.setOutput(output);
  377. }
  378. if (directory != null) {
  379. ant.setDir(directory);
  380. }
  381. ant.setInheritAll(inheritAll);
  382. for (Enumeration i = properties.elements(); i.hasMoreElements();) {
  383. copyProperty(ant.createProperty(), (Property) i.nextElement());
  384. }
  385. for (Enumeration i = propertySets.elements(); i.hasMoreElements();) {
  386. ant.addPropertyset((PropertySet) i.nextElement());
  387. }
  388. ant.setInheritRefs(inheritRefs);
  389. for (Enumeration i = references.elements(); i.hasMoreElements();) {
  390. ant.addReference((Ant.Reference) i.nextElement());
  391. }
  392. return ant;
  393. }
  394. /**
  395. * Assigns an Ant property to another.
  396. *
  397. * @param to the destination property whose content is modified.
  398. * @param from the source property whose content is copied.
  399. */
  400. private static void copyProperty(Property to, Property from) {
  401. to.setName(from.getName());
  402. if (from.getValue() != null) {
  403. to.setValue(from.getValue());
  404. }
  405. if (from.getFile() != null) {
  406. to.setFile(from.getFile());
  407. }
  408. if (from.getResource() != null) {
  409. to.setResource(from.getResource());
  410. }
  411. if (from.getPrefix() != null) {
  412. to.setPrefix(from.getPrefix());
  413. }
  414. if (from.getRefid() != null) {
  415. to.setRefid(from.getRefid());
  416. }
  417. if (from.getEnvironment() != null) {
  418. to.setEnvironment(from.getEnvironment());
  419. }
  420. if (from.getClasspath() != null) {
  421. to.setClasspath(from.getClasspath());
  422. }
  423. }
  424. } // END class SubAnt