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 40 kB

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