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.

Project.java 36 kB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 1999 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", "Tomcat", 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;
  55. import java.io.*;
  56. import java.util.*;
  57. import java.text.*;
  58. /**
  59. * Central representation of an Ant project. This class defines a
  60. * Ant project with all of it's targets and tasks. It also provides
  61. * the mechanism to kick off a build using a particular target name.
  62. * <p>
  63. * This class also encapsulates methods which allow Files to be refered
  64. * to using abstract path names which are translated to native system
  65. * file paths at runtime as well as defining various project properties.
  66. *
  67. * @author duncan@x180.com
  68. */
  69. public class Project {
  70. public static final int MSG_ERR = 0;
  71. public static final int MSG_WARN = 1;
  72. public static final int MSG_INFO = 2;
  73. public static final int MSG_VERBOSE = 3;
  74. public static final int MSG_DEBUG = 4;
  75. // private set of constants to represent the state
  76. // of a DFS of the Target dependencies
  77. private static final String VISITING = "VISITING";
  78. private static final String VISITED = "VISITED";
  79. private static String javaVersion;
  80. public static final String JAVA_1_0 = "1.0";
  81. public static final String JAVA_1_1 = "1.1";
  82. public static final String JAVA_1_2 = "1.2";
  83. public static final String JAVA_1_3 = "1.3";
  84. public static final String TOKEN_START = "@";
  85. public static final String TOKEN_END = "@";
  86. private String name;
  87. private Hashtable properties = new Hashtable();
  88. private Hashtable userProperties = new Hashtable();
  89. private Hashtable references = new Hashtable();
  90. private String defaultTarget;
  91. private Hashtable dataClassDefinitions = new Hashtable();
  92. private Hashtable taskClassDefinitions = new Hashtable();
  93. private Hashtable targets = new Hashtable();
  94. private Hashtable filters = new Hashtable();
  95. private File baseDir;
  96. private Vector listeners = new Vector();
  97. static {
  98. // Determine the Java version by looking at available classes
  99. // java.lang.StrictMath was introduced in JDK 1.3
  100. // java.lang.ThreadLocal was introduced in JDK 1.2
  101. // java.lang.Void was introduced in JDK 1.1
  102. // Count up version until a NoClassDefFoundError ends the try
  103. try {
  104. javaVersion = JAVA_1_0;
  105. Class.forName("java.lang.Void");
  106. javaVersion = JAVA_1_1;
  107. Class.forName("java.lang.ThreadLocal");
  108. javaVersion = JAVA_1_2;
  109. Class.forName("java.lang.StrictMath");
  110. javaVersion = JAVA_1_3;
  111. } catch (ClassNotFoundException cnfe) {
  112. // swallow as we've hit the max class version that
  113. // we have
  114. }
  115. }
  116. public Project() {
  117. }
  118. /**
  119. * Initialise the project.
  120. *
  121. * This involves setting the default task definitions and loading the
  122. * system properties.
  123. */
  124. public void init() throws BuildException {
  125. setJavaVersionProperty();
  126. String defs = "/org/apache/tools/ant/taskdefs/defaults.properties";
  127. try {
  128. Properties props = new Properties();
  129. InputStream in = this.getClass().getResourceAsStream(defs);
  130. if (in == null) {
  131. throw new BuildException("Can't load default task list");
  132. }
  133. props.load(in);
  134. in.close();
  135. Enumeration enum = props.propertyNames();
  136. while (enum.hasMoreElements()) {
  137. String key = (String) enum.nextElement();
  138. String value = props.getProperty(key);
  139. try {
  140. Class taskClass = Class.forName(value);
  141. addTaskDefinition(key, taskClass);
  142. } catch (NoClassDefFoundError ncdfe) {
  143. // ignore...
  144. } catch (ClassNotFoundException cnfe) {
  145. // ignore...
  146. }
  147. }
  148. } catch (IOException ioe) {
  149. throw new BuildException("Can't load default task list");
  150. }
  151. String dataDefs = "/org/apache/tools/ant/types/defaults.properties";
  152. try{
  153. Properties props = new Properties();
  154. InputStream in = this.getClass().getResourceAsStream(dataDefs);
  155. if (in == null) {
  156. throw new BuildException("Can't load default datatype list");
  157. }
  158. props.load(in);
  159. in.close();
  160. Enumeration enum = props.propertyNames();
  161. while (enum.hasMoreElements()) {
  162. String key = (String) enum.nextElement();
  163. String value = props.getProperty(key);
  164. try {
  165. Class dataClass = Class.forName(value);
  166. addDataTypeDefinition(key, dataClass);
  167. } catch (NoClassDefFoundError ncdfe) {
  168. // ignore...
  169. } catch (ClassNotFoundException cnfe) {
  170. // ignore...
  171. }
  172. }
  173. } catch (IOException ioe) {
  174. throw new BuildException("Can't load default datatype list");
  175. }
  176. Properties systemP = System.getProperties();
  177. Enumeration e = systemP.keys();
  178. while (e.hasMoreElements()) {
  179. String name = (String) e.nextElement();
  180. String value = (String) systemP.get(name);
  181. this.setProperty(name, value);
  182. }
  183. }
  184. public void addBuildListener(BuildListener listener) {
  185. listeners.addElement(listener);
  186. }
  187. public void removeBuildListener(BuildListener listener) {
  188. listeners.removeElement(listener);
  189. }
  190. public Vector getBuildListeners() {
  191. return listeners;
  192. }
  193. public void log(String msg) {
  194. log(msg, MSG_INFO);
  195. }
  196. public void log(String msg, int msgLevel) {
  197. fireMessageLogged(this, msg, msgLevel);
  198. }
  199. public void log(Task task, String msg, int msgLevel) {
  200. fireMessageLogged(task, msg, msgLevel);
  201. }
  202. public void log(Target target, String msg, int msgLevel) {
  203. fireMessageLogged(target, msg, msgLevel);
  204. }
  205. public void setProperty(String name, String value) {
  206. // command line properties take precedence
  207. if (null != userProperties.get(name))
  208. return;
  209. log("Setting project property: " + name + " -> " +
  210. value, MSG_DEBUG);
  211. properties.put(name, value);
  212. }
  213. public void setUserProperty(String name, String value) {
  214. log("Setting ro project property: " + name + " -> " +
  215. value, MSG_DEBUG);
  216. userProperties.put(name, value);
  217. properties.put(name, value);
  218. }
  219. public String getProperty(String name) {
  220. if (name == null) return null;
  221. String property = (String) properties.get(name);
  222. return property;
  223. }
  224. public String getUserProperty(String name) {
  225. if (name == null) return null;
  226. String property = (String) userProperties.get(name);
  227. return property;
  228. }
  229. public Hashtable getProperties() {
  230. return properties;
  231. }
  232. public Hashtable getUserProperties() {
  233. return userProperties;
  234. }
  235. public void setDefaultTarget(String defaultTarget) {
  236. this.defaultTarget = defaultTarget;
  237. }
  238. // deprecated, use setDefault
  239. public String getDefaultTarget() {
  240. return defaultTarget;
  241. }
  242. // match the attribute name
  243. public void setDefault(String defaultTarget) {
  244. this.defaultTarget = defaultTarget;
  245. }
  246. public void setName(String name) {
  247. this.name = name;
  248. }
  249. public String getName() {
  250. return name;
  251. }
  252. public void addFilter(String token, String value) {
  253. if (token == null) return;
  254. log("Setting token to filter: " + token + " -> "
  255. + value, MSG_DEBUG);
  256. this.filters.put(token, value);
  257. }
  258. public Hashtable getFilters() {
  259. return filters;
  260. }
  261. // match basedir attribute in xml
  262. public void setBasedir(String baseD) throws BuildException {
  263. try {
  264. setBaseDir(new File(new File(baseD).getCanonicalPath()));
  265. } catch (IOException ioe) {
  266. String msg = "Can't set basedir " + baseD + " due to " +
  267. ioe.getMessage();
  268. throw new BuildException(msg);
  269. }
  270. }
  271. public void setBaseDir(File baseDir) {
  272. this.baseDir = baseDir;
  273. setProperty( "basedir", baseDir.getAbsolutePath());
  274. String msg = "Project base dir set to: " + baseDir;
  275. log(msg, MSG_VERBOSE);
  276. }
  277. public File getBaseDir() {
  278. if (baseDir == null) {
  279. try {
  280. setBasedir(".");
  281. } catch (BuildException ex) {
  282. ex.printStackTrace();
  283. }
  284. }
  285. return baseDir;
  286. }
  287. public static String getJavaVersion() {
  288. return javaVersion;
  289. }
  290. public void setJavaVersionProperty() {
  291. setProperty("ant.java.version", javaVersion);
  292. // sanity check
  293. if (javaVersion == JAVA_1_0) {
  294. throw new BuildException("Ant cannot work on Java 1.0");
  295. }
  296. log("Detected Java Version: " + javaVersion, MSG_VERBOSE);
  297. log("Detected OS: " + System.getProperty("os.name"), MSG_VERBOSE);
  298. }
  299. public void addTaskDefinition(String taskName, Class taskClass) {
  300. String msg = " +User task: " + taskName + " " + taskClass.getName();
  301. log(msg, MSG_DEBUG);
  302. taskClassDefinitions.put(taskName, taskClass);
  303. }
  304. public Hashtable getTaskDefinitions() {
  305. return taskClassDefinitions;
  306. }
  307. public void addDataTypeDefinition(String typeName, Class typeClass) {
  308. String msg = " +User datatype: " + typeName + " " + typeClass.getName();
  309. log(msg, MSG_DEBUG);
  310. dataClassDefinitions.put(typeName, typeClass);
  311. }
  312. public Hashtable getDataTypeDefinitions() {
  313. return dataClassDefinitions;
  314. }
  315. /**
  316. * This call expects to add a <em>new</em> Target.
  317. * @param target is the Target to be added to the current
  318. * Project.
  319. * @exception BuildException if the Target already exists
  320. * in the project.
  321. * @see Project#addOrReplaceTarget to replace existing Targets.
  322. */
  323. public void addTarget(Target target) {
  324. String name = target.getName();
  325. if (targets.get(name) != null) {
  326. throw new BuildException("Duplicate target: `"+name+"'");
  327. }
  328. addOrReplaceTarget(name, target);
  329. }
  330. /**
  331. * This call expects to add a <em>new</em> Target.
  332. * @param target is the Target to be added to the current
  333. * Project.
  334. * @param targetName is the name to use for the Target
  335. * @exception BuildException if the Target already exists
  336. * in the project.
  337. * @see Project#addOrReplaceTarget to replace existing Targets.
  338. */
  339. public void addTarget(String targetName, Target target)
  340. throws BuildException {
  341. if (targets.get(targetName) != null) {
  342. throw new BuildException("Duplicate target: `"+targetName+"'");
  343. }
  344. addOrReplaceTarget(targetName, target);
  345. }
  346. /**
  347. * @param target is the Target to be added or replaced in
  348. * the current Project.
  349. */
  350. public void addOrReplaceTarget(Target target) {
  351. addOrReplaceTarget(target.getName(), target);
  352. }
  353. /**
  354. * @param target is the Target to be added/replaced in
  355. * the current Project.
  356. * @param targetName is the name to use for the Target
  357. */
  358. public void addOrReplaceTarget(String targetName, Target target) {
  359. String msg = " +Target: " + targetName;
  360. log(msg, MSG_DEBUG);
  361. target.setProject(this);
  362. targets.put(targetName, target);
  363. }
  364. public Hashtable getTargets() {
  365. return targets;
  366. }
  367. public Task createTask(String taskType) throws BuildException {
  368. Class c = (Class) taskClassDefinitions.get(taskType);
  369. if (c == null)
  370. return null;
  371. try {
  372. Object o = c.newInstance();
  373. Task task = null;
  374. if( o instanceof Task ) {
  375. task=(Task)o;
  376. } else {
  377. // "Generic" Bean - use the setter pattern
  378. // and an Adapter
  379. TaskAdapter taskA=new TaskAdapter();
  380. taskA.setProxy( o );
  381. task=taskA;
  382. }
  383. task.setProject(this);
  384. task.setTaskType(taskType);
  385. // set default value, can be changed by the user
  386. task.setTaskName(taskType);
  387. String msg = " +Task: " + taskType;
  388. log (msg, MSG_DEBUG);
  389. return task;
  390. } catch (Throwable t) {
  391. String msg = "Could not create task of type: "
  392. + taskType + " due to " + t;
  393. throw new BuildException(msg, t);
  394. }
  395. }
  396. public Object createDataType(String typeName) throws BuildException {
  397. Class c = (Class) dataClassDefinitions.get(typeName);
  398. if (c == null)
  399. return null;
  400. try {
  401. java.lang.reflect.Constructor ctor = null;
  402. boolean noArg = false;
  403. // DataType can have a "no arg" constructor or take a single
  404. // Project argument.
  405. try {
  406. ctor = c.getConstructor(new Class[0]);
  407. noArg = true;
  408. } catch (NoSuchMethodException nse) {
  409. ctor = c.getConstructor(new Class[] {getClass()});
  410. noArg = false;
  411. }
  412. Object o = null;
  413. if (noArg) {
  414. o = ctor.newInstance(new Object[0]);
  415. } else {
  416. o = ctor.newInstance(new Object[] {this});
  417. }
  418. String msg = " +DataType: " + typeName;
  419. log (msg, MSG_DEBUG);
  420. return o;
  421. } catch (java.lang.reflect.InvocationTargetException ite) {
  422. Throwable t = ite.getTargetException();
  423. String msg = "Could not create datatype of type: "
  424. + typeName + " due to " + t;
  425. throw new BuildException(msg, t);
  426. } catch (Throwable t) {
  427. String msg = "Could not create datatype of type: "
  428. + typeName + " due to " + t;
  429. throw new BuildException(msg, t);
  430. }
  431. }
  432. public void executeTargets(Vector targetNames) throws BuildException {
  433. Throwable error = null;
  434. for (int i = 0; i < targetNames.size(); i++) {
  435. executeTarget((String)targetNames.elementAt(i));
  436. }
  437. }
  438. public void executeTarget(String targetName) throws BuildException {
  439. // sanity check ourselves, if we've been asked to build nothing
  440. // then we should complain
  441. if (targetName == null) {
  442. String msg = "No target specified";
  443. throw new BuildException(msg);
  444. }
  445. // Sort the dependency tree, and run everything from the
  446. // beginning until we hit our targetName.
  447. // Sorting checks if all the targets (and dependencies)
  448. // exist, and if there is any cycle in the dependency
  449. // graph.
  450. Vector sortedTargets = topoSort(targetName, targets);
  451. int curidx = 0;
  452. Target curtarget;
  453. do {
  454. curtarget = (Target) sortedTargets.elementAt(curidx++);
  455. runTarget(curtarget);
  456. } while (!curtarget.getName().equals(targetName));
  457. }
  458. public File resolveFile(String fileName) {
  459. fileName = fileName.replace('/', File.separatorChar).replace('\\', File.separatorChar);
  460. // deal with absolute files
  461. if (fileName.startsWith(File.separator)) {
  462. try {
  463. return new File(new File(fileName).getCanonicalPath());
  464. } catch (IOException e) {
  465. log("IOException getting canonical path for " + fileName
  466. + ": " + e.getMessage(), MSG_ERR);
  467. return new File(fileName);
  468. }
  469. }
  470. // Eliminate consecutive slashes after the drive spec
  471. if (fileName.length() >= 2 &&
  472. Character.isLetter(fileName.charAt(0)) &&
  473. fileName.charAt(1) == ':') {
  474. char[] ca = fileName.replace('/', '\\').toCharArray();
  475. char c;
  476. StringBuffer sb = new StringBuffer();
  477. for (int i = 0; i < ca.length; i++) {
  478. if ((ca[i] != '\\') ||
  479. (ca[i] == '\\' &&
  480. i > 0 &&
  481. ca[i - 1] != '\\')) {
  482. if (i == 0 &&
  483. Character.isLetter(ca[i]) &&
  484. i < ca.length - 1 &&
  485. ca[i + 1] == ':') {
  486. c = Character.toUpperCase(ca[i]);
  487. } else {
  488. c = ca[i];
  489. }
  490. sb.append(c);
  491. }
  492. }
  493. return new File(sb.toString());
  494. }
  495. File file = new File(baseDir.getAbsolutePath());
  496. StringTokenizer tok = new StringTokenizer(fileName, File.separator, false);
  497. while (tok.hasMoreTokens()) {
  498. String part = tok.nextToken();
  499. if (part.equals("..")) {
  500. file = new File(file.getParent());
  501. } else if (part.equals(".")) {
  502. // Do nothing here
  503. } else {
  504. file = new File(file, part);
  505. }
  506. }
  507. try {
  508. return new File(file.getCanonicalPath());
  509. }
  510. catch (IOException e) {
  511. log("IOException getting canonical path for " + file + ": " +
  512. e.getMessage(), MSG_ERR);
  513. return new File(file.getAbsolutePath());
  514. }
  515. }
  516. /**
  517. * Translate a path into its native (platform specific) format.
  518. * <p>
  519. * This method uses the PathTokenizer class to separate the input path
  520. * into its components. This handles DOS style paths in a relatively
  521. * sensible way. The file separators are then converted to their platform
  522. * specific versions.
  523. *
  524. * @param to_process the path to be converted
  525. *
  526. * @return the native version of to_process or
  527. * an empty string if to_process is null or empty
  528. */
  529. static public String translatePath(String to_process) {
  530. if ( to_process == null || to_process.length() == 0 ) {
  531. return "";
  532. }
  533. StringBuffer path = new StringBuffer(to_process.length() + 50);
  534. PathTokenizer tokenizer = new PathTokenizer(to_process);
  535. while (tokenizer.hasMoreTokens()) {
  536. String pathComponent = tokenizer.nextToken();
  537. pathComponent = pathComponent.replace('/', File.separatorChar);
  538. pathComponent = pathComponent.replace('\\', File.separatorChar);
  539. if (path.length() != 0) {
  540. path.append(File.pathSeparatorChar);
  541. }
  542. path.append(pathComponent);
  543. }
  544. return path.toString();
  545. }
  546. /**
  547. * Convienence method to copy a file from a source to a destination.
  548. * No filtering is performed.
  549. *
  550. * @throws IOException
  551. */
  552. public void copyFile(String sourceFile, String destFile) throws IOException {
  553. copyFile(new File(sourceFile), new File(destFile), false);
  554. }
  555. /**
  556. * Convienence method to copy a file from a source to a destination
  557. * specifying if token filtering must be used.
  558. *
  559. * @throws IOException
  560. */
  561. public void copyFile(String sourceFile, String destFile, boolean filtering)
  562. throws IOException
  563. {
  564. copyFile(new File(sourceFile), new File(destFile), filtering);
  565. }
  566. /**
  567. * Convienence method to copy a file from a source to a
  568. * destination specifying if token filtering must be used and if
  569. * source files may overwrite newer destination files.
  570. *
  571. * @throws IOException
  572. */
  573. public void copyFile(String sourceFile, String destFile, boolean filtering,
  574. boolean overwrite) throws IOException {
  575. copyFile(new File(sourceFile), new File(destFile), filtering,
  576. overwrite);
  577. }
  578. /**
  579. * Convienence method to copy a file from a source to a destination.
  580. * No filtering is performed.
  581. *
  582. * @throws IOException
  583. */
  584. public void copyFile(File sourceFile, File destFile) throws IOException {
  585. copyFile(sourceFile, destFile, false);
  586. }
  587. /**
  588. * Convienence method to copy a file from a source to a destination
  589. * specifying if token filtering must be used.
  590. *
  591. * @throws IOException
  592. */
  593. public void copyFile(File sourceFile, File destFile, boolean filtering)
  594. throws IOException
  595. {
  596. copyFile(sourceFile, destFile, filtering, false);
  597. }
  598. /**
  599. * Convienence method to copy a file from a source to a
  600. * destination specifying if token filtering must be used and if
  601. * source files may overwrite newer destination files.
  602. *
  603. * @throws IOException
  604. */
  605. public void copyFile(File sourceFile, File destFile, boolean filtering,
  606. boolean overwrite) throws IOException {
  607. if (overwrite ||
  608. destFile.lastModified() < sourceFile.lastModified()) {
  609. log("Copy: " + sourceFile.getAbsolutePath() + " > "
  610. + destFile.getAbsolutePath(), MSG_VERBOSE);
  611. // ensure that parent dir of dest file exists!
  612. // not using getParentFile method to stay 1.1 compat
  613. File parent = new File(destFile.getParent());
  614. if (!parent.exists()) {
  615. parent.mkdirs();
  616. }
  617. if (filtering) {
  618. BufferedReader in = new BufferedReader(new FileReader(sourceFile));
  619. BufferedWriter out = new BufferedWriter(new FileWriter(destFile));
  620. int length;
  621. String newline = null;
  622. String line = in.readLine();
  623. while (line != null) {
  624. if (line.length() == 0) {
  625. out.newLine();
  626. } else {
  627. newline = replace(line, filters);
  628. out.write(newline);
  629. out.newLine();
  630. }
  631. line = in.readLine();
  632. }
  633. out.close();
  634. in.close();
  635. } else {
  636. FileInputStream in = new FileInputStream(sourceFile);
  637. FileOutputStream out = new FileOutputStream(destFile);
  638. byte[] buffer = new byte[8 * 1024];
  639. int count = 0;
  640. do {
  641. out.write(buffer, 0, count);
  642. count = in.read(buffer, 0, buffer.length);
  643. } while (count != -1);
  644. in.close();
  645. out.close();
  646. }
  647. }
  648. }
  649. /**
  650. * Does replacement on the given string using the given token table.
  651. *
  652. * @returns the string with the token replaced.
  653. */
  654. private String replace(String s, Hashtable tokens) {
  655. int index = s.indexOf(TOKEN_START);
  656. if (index > -1) {
  657. try {
  658. StringBuffer b = new StringBuffer();
  659. int i = 0;
  660. String token = null;
  661. String value = null;
  662. do {
  663. int endIndex = s.indexOf(TOKEN_END,
  664. index + TOKEN_START.length() + 1);
  665. if (endIndex == -1) {
  666. break;
  667. }
  668. token = s.substring(index + TOKEN_START.length(), endIndex);
  669. b.append(s.substring(i, index));
  670. if (tokens.containsKey(token)) {
  671. value = (String) tokens.get(token);
  672. log("Replacing: " + TOKEN_START + token + TOKEN_END + " -> " + value, MSG_VERBOSE);
  673. b.append(value);
  674. i = index + TOKEN_START.length() + token.length() + TOKEN_END.length();
  675. } else {
  676. // just append TOKEN_START and search further
  677. b.append(TOKEN_START);
  678. i = index + TOKEN_START.length();
  679. }
  680. } while ((index = s.indexOf(TOKEN_START, i)) > -1);
  681. b.append(s.substring(i));
  682. return b.toString();
  683. } catch (StringIndexOutOfBoundsException e) {
  684. return s;
  685. }
  686. } else {
  687. return s;
  688. }
  689. }
  690. /**
  691. * returns the boolean equivalent of a string, which is considered true
  692. * if either "on", "true", or "yes" is found, ignoring case.
  693. */
  694. public static boolean toBoolean(String s) {
  695. return (s.equalsIgnoreCase("on") ||
  696. s.equalsIgnoreCase("true") ||
  697. s.equalsIgnoreCase("yes"));
  698. }
  699. // Given a string defining a target name, and a Hashtable
  700. // containing the "name to Target" mapping, pick out the
  701. // Target and execute it.
  702. public void runTarget(Target target)
  703. throws BuildException {
  704. try {
  705. fireTargetStarted(target);
  706. target.execute();
  707. fireTargetFinished(target, null);
  708. }
  709. catch(RuntimeException exc) {
  710. fireTargetFinished(target, exc);
  711. throw exc;
  712. }
  713. }
  714. /**
  715. * Topologically sort a set of Targets.
  716. * @param root is the (String) name of the root Target. The sort is
  717. * created in such a way that the sequence of Targets uptil the root
  718. * target is the minimum possible such sequence.
  719. * @param targets is a Hashtable representing a "name to Target" mapping
  720. * @return a Vector of Strings with the names of the targets in
  721. * sorted order.
  722. * @exception BuildException if there is a cyclic dependency among the
  723. * Targets, or if a Target does not exist.
  724. */
  725. private final Vector topoSort(String root, Hashtable targets)
  726. throws BuildException {
  727. Vector ret = new Vector();
  728. Hashtable state = new Hashtable();
  729. Stack visiting = new Stack();
  730. // We first run a DFS based sort using the root as the starting node.
  731. // This creates the minimum sequence of Targets to the root node.
  732. // We then do a sort on any remaining unVISITED targets.
  733. // This is unnecessary for doing our build, but it catches
  734. // circular dependencies or missing Targets on the entire
  735. // dependency tree, not just on the Targets that depend on the
  736. // build Target.
  737. tsort(root, targets, state, visiting, ret);
  738. log("Build sequence for target `"+root+"' is "+ret, MSG_VERBOSE);
  739. for (Enumeration en=targets.keys(); en.hasMoreElements();) {
  740. String curTarget = (String)(en.nextElement());
  741. String st = (String) state.get(curTarget);
  742. if (st == null) {
  743. tsort(curTarget, targets, state, visiting, ret);
  744. }
  745. else if (st == VISITING) {
  746. throw new RuntimeException("Unexpected node in visiting state: "+curTarget);
  747. }
  748. }
  749. log("Complete build sequence is "+ret, MSG_VERBOSE);
  750. return ret;
  751. }
  752. // one step in a recursive DFS traversal of the Target dependency tree.
  753. // - The Hashtable "state" contains the state (VISITED or VISITING or null)
  754. // of all the target names.
  755. // - The Stack "visiting" contains a stack of target names that are
  756. // currently on the DFS stack. (NB: the target names in "visiting" are
  757. // exactly the target names in "state" that are in the VISITING state.)
  758. // 1. Set the current target to the VISITING state, and push it onto
  759. // the "visiting" stack.
  760. // 2. Throw a BuildException if any child of the current node is
  761. // in the VISITING state (implies there is a cycle.) It uses the
  762. // "visiting" Stack to construct the cycle.
  763. // 3. If any children have not been VISITED, tsort() the child.
  764. // 4. Add the current target to the Vector "ret" after the children
  765. // have been visited. Move the current target to the VISITED state.
  766. // "ret" now contains the sorted sequence of Targets upto the current
  767. // Target.
  768. private final void tsort(String root, Hashtable targets,
  769. Hashtable state, Stack visiting,
  770. Vector ret)
  771. throws BuildException {
  772. state.put(root, VISITING);
  773. visiting.push(root);
  774. Target target = (Target)(targets.get(root));
  775. // Make sure we exist
  776. if (target == null) {
  777. StringBuffer sb = new StringBuffer("Target `");
  778. sb.append(root);
  779. sb.append("' does not exist in this project. ");
  780. visiting.pop();
  781. if (!visiting.empty()) {
  782. String parent = (String)visiting.peek();
  783. sb.append("It is used from target `");
  784. sb.append(parent);
  785. sb.append("'.");
  786. }
  787. throw new BuildException(new String(sb));
  788. }
  789. for (Enumeration en=target.getDependencies(); en.hasMoreElements();) {
  790. String cur = (String) en.nextElement();
  791. String m=(String)state.get(cur);
  792. if (m == null) {
  793. // Not been visited
  794. tsort(cur, targets, state, visiting, ret);
  795. }
  796. else if (m == VISITING) {
  797. // Currently visiting this node, so have a cycle
  798. throw makeCircularException(cur, visiting);
  799. }
  800. }
  801. String p = (String) visiting.pop();
  802. if (root != p) {
  803. throw new RuntimeException("Unexpected internal error: expected to pop "+root+" but got "+p);
  804. }
  805. state.put(root, VISITED);
  806. ret.addElement(target);
  807. }
  808. private static BuildException makeCircularException(String end, Stack stk) {
  809. StringBuffer sb = new StringBuffer("Circular dependency: ");
  810. sb.append(end);
  811. String c;
  812. do {
  813. c = (String)stk.pop();
  814. sb.append(" <- ");
  815. sb.append(c);
  816. } while(!c.equals(end));
  817. return new BuildException(new String(sb));
  818. }
  819. public void addReference(String name, Object value) {
  820. references.put(name,value);
  821. }
  822. public Hashtable getReferences() {
  823. return references;
  824. }
  825. protected void fireBuildStarted() {
  826. BuildEvent event = new BuildEvent(this);
  827. for (int i = 0; i < listeners.size(); i++) {
  828. BuildListener listener = (BuildListener) listeners.elementAt(i);
  829. listener.buildStarted(event);
  830. }
  831. }
  832. protected void fireBuildFinished(Throwable exception) {
  833. BuildEvent event = new BuildEvent(this);
  834. event.setException(exception);
  835. for (int i = 0; i < listeners.size(); i++) {
  836. BuildListener listener = (BuildListener) listeners.elementAt(i);
  837. listener.buildFinished(event);
  838. }
  839. }
  840. protected void fireTargetStarted(Target target) {
  841. BuildEvent event = new BuildEvent(target);
  842. for (int i = 0; i < listeners.size(); i++) {
  843. BuildListener listener = (BuildListener) listeners.elementAt(i);
  844. listener.targetStarted(event);
  845. }
  846. }
  847. protected void fireTargetFinished(Target target, Throwable exception) {
  848. BuildEvent event = new BuildEvent(target);
  849. event.setException(exception);
  850. for (int i = 0; i < listeners.size(); i++) {
  851. BuildListener listener = (BuildListener) listeners.elementAt(i);
  852. listener.targetFinished(event);
  853. }
  854. }
  855. protected void fireTaskStarted(Task task) {
  856. BuildEvent event = new BuildEvent(task);
  857. for (int i = 0; i < listeners.size(); i++) {
  858. BuildListener listener = (BuildListener) listeners.elementAt(i);
  859. listener.taskStarted(event);
  860. }
  861. }
  862. protected void fireTaskFinished(Task task, Throwable exception) {
  863. BuildEvent event = new BuildEvent(task);
  864. for (int i = 0; i < listeners.size(); i++) {
  865. BuildListener listener = (BuildListener) listeners.elementAt(i);
  866. listener.taskFinished(event);
  867. }
  868. }
  869. private void fireMessageLoggedEvent(BuildEvent event, String message, int priority) {
  870. event.setMessage(message, priority);
  871. for (int i = 0; i < listeners.size(); i++) {
  872. BuildListener listener = (BuildListener) listeners.elementAt(i);
  873. listener.messageLogged(event);
  874. }
  875. }
  876. protected void fireMessageLogged(Project project, String message, int priority) {
  877. BuildEvent event = new BuildEvent(project);
  878. fireMessageLoggedEvent(event, message, priority);
  879. }
  880. protected void fireMessageLogged(Target target, String message, int priority) {
  881. BuildEvent event = new BuildEvent(target);
  882. fireMessageLoggedEvent(event, message, priority);
  883. }
  884. protected void fireMessageLogged(Task task, String message, int priority) {
  885. BuildEvent event = new BuildEvent(task);
  886. fireMessageLoggedEvent(event, message, priority);
  887. }
  888. }