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

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