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

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