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.

Ant.java 21 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant.taskdefs;
  55. import java.io.File;
  56. import java.io.FileOutputStream;
  57. import java.io.IOException;
  58. import java.io.PrintStream;
  59. import java.lang.reflect.Method;
  60. import java.util.Enumeration;
  61. import java.util.Hashtable;
  62. import java.util.Vector;
  63. import org.apache.tools.ant.BuildException;
  64. import org.apache.tools.ant.BuildListener;
  65. import org.apache.tools.ant.DefaultLogger;
  66. import org.apache.tools.ant.Project;
  67. import org.apache.tools.ant.ProjectComponent;
  68. import org.apache.tools.ant.ProjectHelper;
  69. import org.apache.tools.ant.Task;
  70. import org.apache.tools.ant.util.FileUtils;
  71. /**
  72. * Build a sub-project.
  73. *
  74. * <pre>
  75. * &lt;target name=&quot;foo&quot; depends=&quot;init&quot;&gt;
  76. * &lt;ant antfile=&quot;build.xml&quot; target=&quot;bar&quot; &gt;
  77. * &lt;property name=&quot;property1&quot; value=&quot;aaaaa&quot; /&gt;
  78. * &lt;property name=&quot;foo&quot; value=&quot;baz&quot; /&gt;
  79. * &lt;/ant&gt;</SPAN>
  80. * &lt;/target&gt;</SPAN>
  81. *
  82. * &lt;target name=&quot;bar&quot; depends=&quot;init&quot;&gt;
  83. * &lt;echo message=&quot;prop is ${property1} ${foo}&quot; /&gt;
  84. * &lt;/target&gt;
  85. * </pre>
  86. *
  87. *
  88. * @author Costin Manolache
  89. *
  90. * @since Ant 1.1
  91. *
  92. * @ant.task category="control"
  93. */
  94. public class Ant extends Task {
  95. /** the basedir where is executed the build file */
  96. private File dir = null;
  97. /**
  98. * the build.xml file (can be absolute) in this case dir will be
  99. * ignored
  100. */
  101. private String antFile = null;
  102. /** the target to call if any */
  103. private String target = null;
  104. /** the output */
  105. private String output = null;
  106. /** should we inherit properties from the parent ? */
  107. private boolean inheritAll = true;
  108. /** should we inherit references from the parent ? */
  109. private boolean inheritRefs = false;
  110. /** the properties to pass to the new project */
  111. private Vector properties = new Vector();
  112. /** the references to pass to the new project */
  113. private Vector references = new Vector();
  114. /** the temporary project created to run the build file */
  115. private Project newProject;
  116. /** The stream to which output is to be written. */
  117. private PrintStream out = null;
  118. /**
  119. * If true, pass all properties to the new Ant project.
  120. * Defaults to true.
  121. */
  122. public void setInheritAll(boolean value) {
  123. inheritAll = value;
  124. }
  125. /**
  126. * If true, pass all references to the new Ant project.
  127. * Defaults to false.
  128. */
  129. public void setInheritRefs(boolean value) {
  130. inheritRefs = value;
  131. }
  132. /**
  133. * Creates a Project instance for the project to call.
  134. */
  135. public void init() {
  136. newProject = new Project();
  137. newProject.setJavaVersionProperty();
  138. newProject.addTaskDefinition("property",
  139. (Class) getProject().getTaskDefinitions()
  140. .get("property"));
  141. }
  142. /**
  143. * Called in execute or createProperty if newProject is null.
  144. *
  145. * <p>This can happen if the same instance of this task is run
  146. * twice as newProject is set to null at the end of execute (to
  147. * save memory and help the GC).</p>
  148. *
  149. * <p>Sets all properties that have been defined as nested
  150. * property elements.</p>
  151. */
  152. private void reinit() {
  153. init();
  154. final int count = properties.size();
  155. for (int i = 0; i < count; i++) {
  156. Property p = (Property) properties.elementAt(i);
  157. Property newP = (Property) newProject.createTask("property");
  158. newP.setName(p.getName());
  159. if (p.getValue() != null) {
  160. newP.setValue(p.getValue());
  161. }
  162. if (p.getFile() != null) {
  163. newP.setFile(p.getFile());
  164. }
  165. if (p.getResource() != null) {
  166. newP.setResource(p.getResource());
  167. }
  168. if (p.getPrefix() != null) {
  169. newP.setPrefix(p.getPrefix());
  170. }
  171. if (p.getRefid() != null) {
  172. newP.setRefid(p.getRefid());
  173. }
  174. if (p.getEnvironment() != null) {
  175. newP.setEnvironment(p.getEnvironment());
  176. }
  177. if (p.getClasspath() != null) {
  178. newP.setClasspath(p.getClasspath());
  179. }
  180. properties.setElementAt(newP, i);
  181. }
  182. }
  183. /**
  184. * Attaches the build listeners of the current project to the new
  185. * project, configures a possible logfile, transfers task and
  186. * data-type definitions, transfers properties (either all or just
  187. * the ones specified as user properties to the current project,
  188. * depending on inheritall), transfers the input handler.
  189. */
  190. private void initializeProject() {
  191. newProject.setInputHandler(getProject().getInputHandler());
  192. Vector listeners = getProject().getBuildListeners();
  193. final int count = listeners.size();
  194. for (int i = 0; i < count; i++) {
  195. newProject.addBuildListener((BuildListener) listeners.elementAt(i));
  196. }
  197. if (output != null) {
  198. File outfile = null;
  199. if (dir != null) {
  200. outfile = FileUtils.newFileUtils().resolveFile(dir, output);
  201. } else {
  202. outfile = getProject().resolveFile(output);
  203. }
  204. try {
  205. out = new PrintStream(new FileOutputStream(outfile));
  206. DefaultLogger logger = new DefaultLogger();
  207. logger.setMessageOutputLevel(Project.MSG_INFO);
  208. logger.setOutputPrintStream(out);
  209. logger.setErrorPrintStream(out);
  210. newProject.addBuildListener(logger);
  211. } catch (IOException ex) {
  212. log("Ant: Can't set output to " + output);
  213. }
  214. }
  215. Hashtable taskdefs = getProject().getTaskDefinitions();
  216. Enumeration et = taskdefs.keys();
  217. while (et.hasMoreElements()) {
  218. String taskName = (String) et.nextElement();
  219. if (taskName.equals("property")) {
  220. // we have already added this taskdef in #init
  221. continue;
  222. }
  223. Class taskClass = (Class) taskdefs.get(taskName);
  224. newProject.addTaskDefinition(taskName, taskClass);
  225. }
  226. Hashtable typedefs = getProject().getDataTypeDefinitions();
  227. Enumeration e = typedefs.keys();
  228. while (e.hasMoreElements()) {
  229. String typeName = (String) e.nextElement();
  230. Class typeClass = (Class) typedefs.get(typeName);
  231. newProject.addDataTypeDefinition(typeName, typeClass);
  232. }
  233. // set user-defined properties
  234. getProject().copyUserProperties(newProject);
  235. if (!inheritAll) {
  236. // set Java built-in properties separately,
  237. // b/c we won't inherit them.
  238. newProject.setSystemProperties();
  239. } else {
  240. // set all properties from calling project
  241. Hashtable props = getProject().getProperties();
  242. e = props.keys();
  243. while (e.hasMoreElements()) {
  244. String arg = e.nextElement().toString();
  245. if ("basedir".equals(arg) || "ant.file".equals(arg)) {
  246. // basedir and ant.file get special treatment in execute()
  247. continue;
  248. }
  249. String value = props.get(arg).toString();
  250. // don't re-set user properties, avoid the warning message
  251. if (newProject.getProperty(arg) == null){
  252. // no user property
  253. newProject.setNewProperty(arg, value);
  254. }
  255. }
  256. }
  257. }
  258. /**
  259. * Pass output sent to System.out to the new project.
  260. *
  261. * @since Ant 1.5
  262. */
  263. protected void handleOutput(String line) {
  264. if (newProject != null) {
  265. newProject.demuxOutput(line, false);
  266. } else {
  267. super.handleOutput(line);
  268. }
  269. }
  270. /**
  271. * Pass output sent to System.err to the new project.
  272. *
  273. * @since Ant 1.5
  274. */
  275. protected void handleErrorOutput(String line) {
  276. if (newProject != null) {
  277. newProject.demuxOutput(line, true);
  278. } else {
  279. super.handleErrorOutput(line);
  280. }
  281. }
  282. /**
  283. * Do the execution.
  284. */
  285. public void execute() throws BuildException {
  286. File savedDir = dir;
  287. String savedAntFile = antFile;
  288. String savedTarget = target;
  289. try {
  290. if (newProject == null) {
  291. reinit();
  292. }
  293. if ((dir == null) && (inheritAll)) {
  294. dir = getProject().getBaseDir();
  295. }
  296. initializeProject();
  297. if (dir != null) {
  298. newProject.setBaseDir(dir);
  299. if (savedDir != null) { // has been set explicitly
  300. newProject.setInheritedProperty("basedir" ,
  301. dir.getAbsolutePath());
  302. }
  303. } else {
  304. dir = getProject().getBaseDir();
  305. }
  306. overrideProperties();
  307. if (antFile == null) {
  308. antFile = "build.xml";
  309. }
  310. File file = FileUtils.newFileUtils().resolveFile(dir, antFile);
  311. antFile = file.getAbsolutePath();
  312. log("calling target " + (target != null ? target : "[default]")
  313. + " in build file " + antFile.toString(),
  314. Project.MSG_VERBOSE);
  315. newProject.setUserProperty("ant.file" , antFile);
  316. ProjectHelper.configureProject(newProject, new File(antFile));
  317. if (target == null) {
  318. target = newProject.getDefaultTarget();
  319. }
  320. // Are we trying to call the target in which we are defined (or
  321. // the build file if this is a top level task)?
  322. if (newProject.getBaseDir().equals(getProject().getBaseDir()) &&
  323. newProject.getProperty("ant.file").equals(getProject().getProperty("ant.file"))
  324. && getOwningTarget() != null
  325. && (getOwningTarget().getName().equals("") ||
  326. getOwningTarget().getName().equals(target))) {
  327. throw new BuildException("ant task calling its own parent "
  328. + "target");
  329. }
  330. addReferences();
  331. if (target != null) {
  332. newProject.executeTarget(target);
  333. } else {
  334. newProject.executeTarget("");
  335. }
  336. } finally {
  337. // help the gc
  338. newProject = null;
  339. Enumeration enum = properties.elements();
  340. while (enum.hasMoreElements()) {
  341. Property p = (Property) enum.nextElement();
  342. p.setProject(null);
  343. }
  344. if (output != null && out != null) {
  345. try {
  346. out.close();
  347. } catch (final Exception e) {
  348. //ignore
  349. }
  350. }
  351. dir = savedDir;
  352. antFile = savedAntFile;
  353. target = savedTarget;
  354. }
  355. }
  356. /**
  357. * Override the properties in the new project with the one
  358. * explicitly defined as nested elements here.
  359. */
  360. private void overrideProperties() throws BuildException {
  361. Enumeration e = properties.elements();
  362. while (e.hasMoreElements()) {
  363. Property p = (Property) e.nextElement();
  364. p.setProject(newProject);
  365. p.execute();
  366. }
  367. getProject().copyInheritedProperties(newProject);
  368. }
  369. /**
  370. * Add the references explicitly defined as nested elements to the
  371. * new project. Also copy over all references that don't override
  372. * existing references in the new project if inheritrefs has been
  373. * requested.
  374. */
  375. private void addReferences() throws BuildException {
  376. Hashtable thisReferences = (Hashtable) getProject().getReferences().clone();
  377. Hashtable newReferences = newProject.getReferences();
  378. Enumeration e;
  379. if (references.size() > 0) {
  380. for (e = references.elements(); e.hasMoreElements();) {
  381. Reference ref = (Reference) e.nextElement();
  382. String refid = ref.getRefId();
  383. if (refid == null) {
  384. throw new BuildException("the refid attribute is required"
  385. + " for reference elements");
  386. }
  387. if (!thisReferences.containsKey(refid)) {
  388. log("Parent project doesn't contain any reference '"
  389. + refid + "'",
  390. Project.MSG_WARN);
  391. continue;
  392. }
  393. thisReferences.remove(refid);
  394. String toRefid = ref.getToRefid();
  395. if (toRefid == null) {
  396. toRefid = refid;
  397. }
  398. copyReference(refid, toRefid);
  399. }
  400. }
  401. // Now add all references that are not defined in the
  402. // subproject, if inheritRefs is true
  403. if (inheritRefs) {
  404. for (e = thisReferences.keys(); e.hasMoreElements();) {
  405. String key = (String) e.nextElement();
  406. if (newReferences.containsKey(key)) {
  407. continue;
  408. }
  409. copyReference(key, key);
  410. }
  411. }
  412. }
  413. /**
  414. * Try to clone and reconfigure the object referenced by oldkey in
  415. * the parent project and add it to the new project with the key
  416. * newkey.
  417. *
  418. * <p>If we cannot clone it, copy the referenced object itself and
  419. * keep our fingers crossed.</p>
  420. */
  421. private void copyReference(String oldKey, String newKey) {
  422. Object orig = getProject().getReference(oldKey);
  423. if (orig == null) {
  424. log("No object referenced by " + oldKey + ". Can't copy to "
  425. + newKey,
  426. Project.MSG_WARN);
  427. return;
  428. }
  429. Class c = orig.getClass();
  430. Object copy = orig;
  431. try {
  432. Method cloneM = c.getMethod("clone", new Class[0]);
  433. if (cloneM != null) {
  434. copy = cloneM.invoke(orig, new Object[0]);
  435. log("Adding clone of reference " + oldKey, Project.MSG_DEBUG);
  436. }
  437. } catch (Exception e) {
  438. // not Clonable
  439. }
  440. if (copy instanceof ProjectComponent) {
  441. ((ProjectComponent) copy).setProject(newProject);
  442. } else {
  443. try {
  444. Method setProjectM =
  445. c.getMethod("setProject", new Class[] {Project.class});
  446. if (setProjectM != null) {
  447. setProjectM.invoke(copy, new Object[] {newProject});
  448. }
  449. } catch (NoSuchMethodException e) {
  450. // ignore this if the class being referenced does not have
  451. // a set project method.
  452. } catch (Exception e2) {
  453. String msg = "Error setting new project instance for "
  454. + "reference with id " + oldKey;
  455. throw new BuildException(msg, e2, getLocation());
  456. }
  457. }
  458. newProject.addReference(newKey, copy);
  459. }
  460. /**
  461. * The directory to use as a base directory for the new Ant project.
  462. * Defaults to the current project's basedir, unless inheritall
  463. * has been set to false, in which case it doesn't have a default
  464. * value. This will override the basedir setting of the called project.
  465. */
  466. public void setDir(File d) {
  467. this.dir = d;
  468. }
  469. /**
  470. * The build file to use.
  471. * Defaults to "build.xml". This file is expected to be a filename relative
  472. * to the dir attribute given.
  473. */
  474. public void setAntfile(String s) {
  475. // @note: it is a string and not a file to handle relative/absolute
  476. // otherwise a relative file will be resolved based on the current
  477. // basedir.
  478. this.antFile = s;
  479. }
  480. /**
  481. * The target of the new Ant project to execute.
  482. * Defaults to the new project's default target.
  483. */
  484. public void setTarget(String s) {
  485. if (s.equals("")) {
  486. throw new BuildException("target attribute must not be empty");
  487. }
  488. this.target = s;
  489. }
  490. /**
  491. * Filename to write the output to.
  492. * This is relative to the value of the dir attribute
  493. * if it has been set or to the base directory of the
  494. * current project otherwise.
  495. */
  496. public void setOutput(String s) {
  497. this.output = s;
  498. }
  499. /**
  500. * Property to pass to the new project.
  501. * The property is passed as a 'user property'
  502. */
  503. public Property createProperty() {
  504. if (newProject == null) {
  505. reinit();
  506. }
  507. Property p = new Property(true, getProject());
  508. p.setProject(newProject);
  509. p.setTaskName("property");
  510. properties.addElement(p);
  511. return p;
  512. }
  513. /**
  514. * Reference element identifying a data type to carry
  515. * over to the new project.
  516. */
  517. public void addReference(Reference r) {
  518. references.addElement(r);
  519. }
  520. /**
  521. * Helper class that implements the nested &lt;reference&gt;
  522. * element of &lt;ant&gt; and &lt;antcall&gt;.
  523. */
  524. public static class Reference
  525. extends org.apache.tools.ant.types.Reference {
  526. /** Creates a reference to be configured by Ant */
  527. public Reference() {
  528. super();
  529. }
  530. private String targetid = null;
  531. /**
  532. * Set the id that this reference to be stored under in the
  533. * new project.
  534. *
  535. * @param targetid the id under which this reference will be passed to
  536. * the new project */
  537. public void setToRefid(String targetid) {
  538. this.targetid = targetid;
  539. }
  540. /**
  541. * Get the id under which this reference will be stored in the new
  542. * project
  543. *
  544. * @return the id of the reference in the new project.
  545. */
  546. public String getToRefid() {
  547. return targetid;
  548. }
  549. }
  550. }