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.

AntBean.java 20 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2000-2002 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.File;
  56. import java.io.FileInputStream;
  57. import java.io.PrintStream;
  58. import java.io.FileOutputStream;
  59. import java.io.IOException;
  60. import java.io.InputStream;
  61. import java.util.Vector;
  62. import java.util.Properties;
  63. import java.util.Enumeration;
  64. /**
  65. * A bean to use to embed ant in a project.
  66. *
  67. * Based on Main.java.
  68. *
  69. * Note: this is the result of refactoring Main. Some methods are not
  70. * usefull for embeded use or may have better names ( I used the
  71. * option name from Main, for consistency ). I marked them with @experimental.
  72. *
  73. * @experimental: the current API is not yet stable.
  74. *
  75. * @author duncan@x180.com
  76. * @author Costin Manolache
  77. * @since ant1.5
  78. */
  79. public class AntBean extends Task {
  80. /** The default build file name */
  81. public final static String DEFAULT_BUILD_FILENAME = "build.xml";
  82. /** Our current message output status. Follows Project.MSG_XXX */
  83. private int msgOutputLevel = Project.MSG_INFO;
  84. /** File that we are using for configuration */
  85. private File buildFile; /** null */
  86. private String searchForThis=null;
  87. /** Stream that we are using for logging */
  88. private PrintStream out = System.out;
  89. /** Stream that we are using for logging error messages */
  90. private PrintStream err = System.err;
  91. /** The build targets */
  92. private Vector targets = new Vector(5);
  93. /** Set of properties that can be used by tasks */
  94. private Properties definedProps = new Properties();
  95. /** Names of classes to add as listeners to project */
  96. private Vector listeners = new Vector(5);
  97. /** File names of property files to load on startup */
  98. private Vector propertyFiles = new Vector(5);
  99. /**
  100. * The Ant logger class. There may be only one logger. It will have the
  101. * right to use the 'out' PrintStream. The class must implements the BuildLogger
  102. * interface
  103. */
  104. private String loggerClassname = null;
  105. /**
  106. * Indicates whether output to the log is to be unadorned.
  107. */
  108. private boolean emacsMode = false;
  109. private ClassLoader coreLoader;
  110. private ProjectHelper helper=null;
  111. private Project newProject=null;
  112. private boolean redirectOutput=true;
  113. public AntBean() {
  114. }
  115. // -------------------- Bean properties --------------------
  116. // extracted from Main's command line processing code
  117. /** Global verbosity level
  118. */
  119. public void setOutputLevel( int level ) {
  120. msgOutputLevel=level;
  121. }
  122. public void setBuildfile( String name ) {
  123. buildFile = new File(name);
  124. }
  125. /** Add a listener class name
  126. */
  127. public void addListener( String s ) {
  128. listeners.addElement(s);
  129. }
  130. /** Set the logger class name ( -logger option in command
  131. * line ).
  132. *
  133. * @experimental LoggerClassName would be a better name
  134. */
  135. public void setLogger( String s ) {
  136. if (loggerClassname != null) {
  137. System.out.println("Only one logger class may be specified.");
  138. return;
  139. }
  140. loggerClassname = s;
  141. }
  142. /** Emacs mode for the output
  143. */
  144. public void setEmacs( boolean b ) {
  145. emacsMode = b;
  146. }
  147. /** The name of the build file to execute, by
  148. * searching in the filesystem.
  149. */
  150. public void setFind( String s ) {
  151. if (s==null) {
  152. searchForThis = s;
  153. } else {
  154. searchForThis = DEFAULT_BUILD_FILENAME;
  155. }
  156. }
  157. /** Same as -propertyfile
  158. */
  159. public void addPropertyfile( String s ) {
  160. propertyFiles.addElement(s);
  161. }
  162. /** Set the core loader, to be used to execute.
  163. */
  164. public void setCoreLoader( ClassLoader coreLoader ) {
  165. coreLoader=coreLoader;
  166. }
  167. /** Add a user-defined property
  168. */
  169. public void setUserProperty( String name, String value ) {
  170. definedProps.put( name, value );
  171. }
  172. /** Add a target to be executed
  173. */
  174. public void addTarget(String arg ) {
  175. targets.addElement(arg);
  176. }
  177. /** Log file. It'll redirect the System output and logs to this
  178. * file. Supported by -logfile argument in ant - probably
  179. * a bad idea if you embed ant in an application.
  180. *
  181. * @experimental - I don't think it's a good idea.
  182. */
  183. public void setLogfile( String name ) {
  184. try {
  185. File logFile = new File(name);
  186. out = new PrintStream(new FileOutputStream(logFile));
  187. err = out;
  188. System.setOut(out);
  189. System.setErr(out);
  190. } catch (IOException ioe) {
  191. String msg = "Cannot write on the specified log file. " +
  192. "Make sure the path exists and you have write permissions.";
  193. System.out.println(msg);
  194. return;
  195. }
  196. }
  197. /** Redirect the output and set a security manager before
  198. * executing ant. Defaults to true for backward comptibility,
  199. * you should set it to false if you embed ant.
  200. */
  201. public void setRedirectOutput( boolean b ) {
  202. redirectOutput=b;
  203. }
  204. // -------------------- Property getters --------------------
  205. /** Return the build file. If it was not explicitely specified, search
  206. * for it in the parent directories
  207. *
  208. * <P>Takes the "find" property as a suffix to append to each
  209. * parent directory in seach of a build file. Once the
  210. * root of the file-system has been reached an exception
  211. * is thrown.
  212. */
  213. public File getBuildFile()
  214. throws BuildException
  215. {
  216. // if buildFile was not specified on the command line,
  217. if (buildFile == null) {
  218. // but -find then search for it
  219. if (searchForThis != null) {
  220. buildFile = findBuildFile(System.getProperty("user.dir"),
  221. searchForThis);
  222. } else {
  223. buildFile = new File(DEFAULT_BUILD_FILENAME);
  224. }
  225. }
  226. getProject().setUserProperty("ant.file" , buildFile.getAbsolutePath() );
  227. return buildFile;
  228. }
  229. /** Return an (initialized) project constructed using the current
  230. * settings.
  231. * This will not load the build.xml file - you can 'load' the
  232. * project object with tasks manually or execute 'standalone'
  233. * tasks in the context of the project.
  234. */
  235. public Project getProject() {
  236. if( newProject!=null )
  237. return newProject;
  238. loadProperties();
  239. helper=ProjectHelper.getProjectHelper();
  240. newProject = helper.createProject(coreLoader);
  241. newProject.setCoreLoader(coreLoader);
  242. addBuildListeners(newProject);
  243. newProject.fireBuildStarted();
  244. newProject.init();
  245. newProject.setUserProperty("ant.version", getAntVersion());
  246. // set user-define properties
  247. Enumeration e = definedProps.keys();
  248. while (e.hasMoreElements()) {
  249. String arg = (String)e.nextElement();
  250. String value = (String)definedProps.get(arg);
  251. newProject.setUserProperty(arg, value);
  252. }
  253. return newProject;
  254. }
  255. private static String antVersion = null;
  256. /** @experimental
  257. * Ant version should be combined with the ProjectHelper version and type,
  258. * since it'll determine the set of features supported by ant ( at the xml
  259. * level ).
  260. */
  261. public static synchronized String getAntVersion() throws BuildException {
  262. if (antVersion == null) {
  263. try {
  264. Properties props = new Properties();
  265. InputStream in =
  266. Main.class.getResourceAsStream("/org/apache/tools/ant/version.txt");
  267. props.load(in);
  268. in.close();
  269. String lSep = System.getProperty("line.separator");
  270. StringBuffer msg = new StringBuffer();
  271. msg.append("Apache Ant version ");
  272. msg.append(props.getProperty("VERSION"));
  273. msg.append(" compiled on ");
  274. msg.append(props.getProperty("DATE"));
  275. antVersion = msg.toString();
  276. } catch (IOException ioe) {
  277. throw new BuildException("Could not load the version information:"
  278. + ioe.getMessage());
  279. } catch (NullPointerException npe) {
  280. throw new BuildException("Could not load the version information.");
  281. }
  282. }
  283. return antVersion;
  284. }
  285. // -------------------- Bean methods --------------------
  286. Throwable error = null;
  287. /** Clean up allocated resources and finish the processing of the
  288. * current Project.
  289. */
  290. public void done() {
  291. newProject.fireBuildFinished(error);
  292. }
  293. /**
  294. * Process an XML file and execute the targets.
  295. *
  296. * This method can be called multiple times, eventually after setting different
  297. * build file and different targets - all executions will happen in the
  298. * same execution context ( project ).
  299. */
  300. public void processBuildXml() throws BuildException {
  301. checkBuildFile();
  302. File buildFile=getBuildFile();
  303. Project newProject=getProject();
  304. // first use the ProjectHelper to create the project object
  305. // from the given build file.
  306. String noParserMessage =
  307. "No JAXP compliant XML parser found. Please visit http://xml.apache.org for a suitable parser";
  308. try {
  309. Class.forName("javax.xml.parsers.SAXParserFactory");
  310. helper.parse(newProject, buildFile);
  311. } catch (NoClassDefFoundError ncdfe) {
  312. throw new BuildException(noParserMessage, ncdfe);
  313. } catch (ClassNotFoundException cnfe) {
  314. throw new BuildException(noParserMessage, cnfe);
  315. } catch (NullPointerException npe) {
  316. throw new BuildException(noParserMessage, npe);
  317. }
  318. // make sure that we have a target to execute
  319. if (targets.size() == 0) {
  320. targets.addElement(newProject.getDefaultTarget());
  321. }
  322. newProject.executeTargets(targets);
  323. }
  324. public void execute() throws BuildException {
  325. try {
  326. if( redirectOutput ) {
  327. pushSystemOut();
  328. }
  329. processBuildXml();
  330. } catch(RuntimeException exc) {
  331. error = exc;
  332. throw exc;
  333. } catch(Error err) {
  334. error = err;
  335. throw err;
  336. } finally {
  337. done();
  338. if( redirectOutput )
  339. popSystemOut();
  340. }
  341. }
  342. // -------------------- Private methods --------------------
  343. private void checkBuildFile() throws BuildException {
  344. File buildFile=getBuildFile();
  345. // make sure buildfile exists
  346. if (!buildFile.exists()) {
  347. System.out.println("Buildfile: " + buildFile + " does not exist!");
  348. throw new BuildException("Build failed");
  349. }
  350. // make sure it's not a directory (this falls into the ultra
  351. // paranoid lets check everything catagory
  352. if (buildFile.isDirectory()) {
  353. System.out.println("What? Buildfile: " + buildFile + " is a dir!");
  354. throw new BuildException("Build failed");
  355. }
  356. // track when we started
  357. if (msgOutputLevel >= Project.MSG_INFO) {
  358. System.out.println("Buildfile: " + buildFile);
  359. }
  360. }
  361. private PrintStream oldErr=null;
  362. private PrintStream oldOut=null;
  363. private SecurityManager oldsm = null;
  364. private void pushSystemOut() {
  365. oldErr = System.err;
  366. oldOut = System.out;
  367. // use a system manager that prevents from System.exit()
  368. // only in JDK > 1.1
  369. if ( !Project.JAVA_1_0.equals(Project.getJavaVersion()) &&
  370. !Project.JAVA_1_1.equals(Project.getJavaVersion()) ){
  371. oldsm = System.getSecurityManager();
  372. //SecurityManager can not be installed here for backwards
  373. //compatability reasons (PD). Needs to be loaded prior to
  374. //ant class if we are going to implement it.
  375. //System.setSecurityManager(new NoExitSecurityManager());
  376. }
  377. System.setOut(new PrintStream(new DemuxOutputStream(getProject(), false)));
  378. System.setErr(new PrintStream(new DemuxOutputStream(getProject(), true)));
  379. }
  380. private void popSystemOut() {
  381. // put back the original security manager
  382. //The following will never eval to true. (PD)
  383. if (oldsm != null){
  384. System.setSecurityManager(oldsm);
  385. }
  386. if( oldOut!=null && oldErr!=null ) {
  387. System.setOut(oldOut);
  388. System.setErr(oldErr);
  389. }
  390. }
  391. protected void addBuildListeners(Project newProject) {
  392. // Add the default listener
  393. newProject.addBuildListener(createLogger());
  394. for (int i = 0; i < listeners.size(); i++) {
  395. String className = (String) listeners.elementAt(i);
  396. try {
  397. BuildListener listener =
  398. (BuildListener) Class.forName(className).newInstance();
  399. newProject.addBuildListener(listener);
  400. }
  401. catch(Throwable exc) {
  402. throw new BuildException("Unable to instantiate listener " + className, exc);
  403. }
  404. }
  405. }
  406. /**
  407. * Creates the default build logger for sending build events to the ant log.
  408. */
  409. protected BuildLogger createLogger() {
  410. BuildLogger logger = null;
  411. if (loggerClassname != null) {
  412. try {
  413. logger = (BuildLogger)(Class.forName(loggerClassname).newInstance());
  414. } catch (ClassCastException e) {
  415. System.err.println("The specified logger class " + loggerClassname +
  416. " does not implement the BuildLogger interface");
  417. throw new RuntimeException();
  418. } catch (Exception e) {
  419. System.err.println("Unable to instantiate specified logger class " +
  420. loggerClassname + " : " + e.getClass().getName());
  421. throw new RuntimeException();
  422. }
  423. }
  424. else {
  425. logger = new DefaultLogger();
  426. }
  427. logger.setMessageOutputLevel(msgOutputLevel);
  428. logger.setOutputPrintStream(out);
  429. logger.setErrorPrintStream(err);
  430. logger.setEmacsMode(emacsMode);
  431. return logger;
  432. }
  433. /** Load all propertyFiles
  434. */
  435. private void loadProperties()
  436. {
  437. // Load the property files specified by -propertyfile
  438. for (int propertyFileIndex=0;
  439. propertyFileIndex < propertyFiles.size();
  440. propertyFileIndex++) {
  441. String filename = (String) propertyFiles.elementAt(propertyFileIndex);
  442. Properties props = new Properties();
  443. FileInputStream fis = null;
  444. try {
  445. fis = new FileInputStream(filename);
  446. props.load(fis);
  447. }
  448. catch (IOException e) {
  449. System.out.println("Could not load property file "
  450. + filename + ": " + e.getMessage());
  451. } finally {
  452. if (fis != null){
  453. try {
  454. fis.close();
  455. } catch (IOException e){
  456. }
  457. }
  458. }
  459. // ensure that -D properties take precedence
  460. Enumeration propertyNames = props.propertyNames();
  461. while (propertyNames.hasMoreElements()) {
  462. String name = (String) propertyNames.nextElement();
  463. if (definedProps.getProperty(name) == null) {
  464. definedProps.put(name, props.getProperty(name));
  465. }
  466. }
  467. }
  468. }
  469. // -------------------- XXX Move to FileUtil --------------------
  470. /**
  471. * Helper to get the parent file for a given file.
  472. *
  473. * <P>Added to simulate File.getParentFile() from JDK 1.2.
  474. *
  475. * @param file File
  476. * @return Parent file or null if none
  477. */
  478. private File getParentFile(File file) {
  479. String filename = file.getAbsolutePath();
  480. file = new File(filename);
  481. filename = file.getParent();
  482. if (filename != null && msgOutputLevel >= Project.MSG_VERBOSE) {
  483. System.out.println("Searching in "+filename);
  484. }
  485. return (filename == null) ? null : new File(filename);
  486. }
  487. /**
  488. * Search parent directories for the build file.
  489. *
  490. * <P>Takes the given target as a suffix to append to each
  491. * parent directory in seach of a build file. Once the
  492. * root of the file-system has been reached an exception
  493. * is thrown.
  494. *
  495. * @param suffix Suffix filename to look for in parents.
  496. * @return A handle to the build file
  497. *
  498. * @exception BuildException Failed to locate a build file
  499. */
  500. private File findBuildFile(String start, String suffix) throws BuildException {
  501. if (msgOutputLevel >= Project.MSG_INFO) {
  502. System.out.println("Searching for " + suffix + " ...");
  503. }
  504. File parent = new File(new File(start).getAbsolutePath());
  505. File file = new File(parent, suffix);
  506. // check if the target file exists in the current directory
  507. while (!file.exists()) {
  508. // change to parent directory
  509. parent = getParentFile(parent);
  510. // if parent is null, then we are at the root of the fs,
  511. // complain that we can't find the build file.
  512. if (parent == null) {
  513. throw new BuildException("Could not locate a build file!");
  514. }
  515. // refresh our file handle
  516. file = new File(parent, suffix);
  517. }
  518. return file;
  519. }
  520. }