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.

AbstractCvsTask.java 23 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801
  1. /*
  2. * Copyright 2002-2006 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.taskdefs;
  18. import java.io.BufferedOutputStream;
  19. import java.io.File;
  20. import java.io.FileOutputStream;
  21. import java.io.IOException;
  22. import java.io.OutputStream;
  23. import java.io.PrintStream;
  24. import java.util.Vector;
  25. import org.apache.tools.ant.BuildException;
  26. import org.apache.tools.ant.Project;
  27. import org.apache.tools.ant.Task;
  28. import org.apache.tools.ant.types.Commandline;
  29. import org.apache.tools.ant.types.Environment;
  30. import org.apache.tools.ant.util.StringUtils;
  31. import org.apache.tools.ant.util.FileUtils;
  32. /**
  33. * original Cvs.java 1.20
  34. *
  35. * NOTE: This implementation has been moved here from Cvs.java with
  36. * the addition of some accessors for extensibility. Another task
  37. * can extend this with some customized output processing.
  38. *
  39. * @since Ant 1.5
  40. */
  41. public abstract class AbstractCvsTask extends Task {
  42. /**
  43. * Default compression level to use, if compression is enabled via
  44. * setCompression( true ).
  45. */
  46. public static final int DEFAULT_COMPRESSION_LEVEL = 3;
  47. private static final int MAXIMUM_COMRESSION_LEVEL = 9;
  48. private Commandline cmd = new Commandline();
  49. /** list of Commandline children */
  50. private Vector vecCommandlines = new Vector();
  51. /**
  52. * the CVSROOT variable.
  53. */
  54. private String cvsRoot;
  55. /**
  56. * the CVS_RSH variable.
  57. */
  58. private String cvsRsh;
  59. /**
  60. * the package/module to check out.
  61. */
  62. private String cvsPackage;
  63. /**
  64. * the tag
  65. */
  66. private String tag;
  67. /**
  68. * the default command.
  69. */
  70. private static final String DEFAULT_COMMAND = "checkout";
  71. /**
  72. * the CVS command to execute.
  73. */
  74. private String command = null;
  75. /**
  76. * suppress information messages.
  77. */
  78. private boolean quiet = false;
  79. /**
  80. * suppress all messages.
  81. */
  82. private boolean reallyquiet = false;
  83. /**
  84. * compression level to use.
  85. */
  86. private int compression = 0;
  87. /**
  88. * report only, don't change any files.
  89. */
  90. private boolean noexec = false;
  91. /**
  92. * CVS port
  93. */
  94. private int port = 0;
  95. /**
  96. * CVS password file
  97. */
  98. private File passFile = null;
  99. /**
  100. * the directory where the checked out files should be placed.
  101. */
  102. private File dest;
  103. /** whether or not to append stdout/stderr to existing files */
  104. private boolean append = false;
  105. /**
  106. * the file to direct standard output from the command.
  107. */
  108. private File output;
  109. /**
  110. * the file to direct standard error from the command.
  111. */
  112. private File error;
  113. /**
  114. * If true it will stop the build if cvs exits with error.
  115. * Default is false. (Iulian)
  116. */
  117. private boolean failOnError = false;
  118. /**
  119. * Create accessors for the following, to allow different handling of
  120. * the output.
  121. */
  122. private ExecuteStreamHandler executeStreamHandler;
  123. private OutputStream outputStream;
  124. private OutputStream errorStream;
  125. /** empty no-arg constructor*/
  126. public AbstractCvsTask() {
  127. super();
  128. }
  129. /**
  130. * sets the handler
  131. * @param handler a handler able of processing the output and error streams from the cvs exe
  132. */
  133. public void setExecuteStreamHandler(ExecuteStreamHandler handler) {
  134. this.executeStreamHandler = handler;
  135. }
  136. /**
  137. * find the handler and instantiate it if it does not exist yet
  138. * @return handler for output and error streams
  139. */
  140. protected ExecuteStreamHandler getExecuteStreamHandler() {
  141. if (this.executeStreamHandler == null) {
  142. setExecuteStreamHandler(new PumpStreamHandler(getOutputStream(),
  143. getErrorStream()));
  144. }
  145. return this.executeStreamHandler;
  146. }
  147. /**
  148. * sets a stream to which the output from the cvs executable should be sent
  149. * @param outputStream stream to which the stdout from cvs should go
  150. */
  151. protected void setOutputStream(OutputStream outputStream) {
  152. this.outputStream = outputStream;
  153. }
  154. /**
  155. * access the stream to which the stdout from cvs should go
  156. * if this stream has already been set, it will be returned
  157. * if the stream has not yet been set, if the attribute output
  158. * has been set, the output stream will go to the output file
  159. * otherwise the output will go to ant's logging system
  160. * @return output stream to which cvs' stdout should go to
  161. */
  162. protected OutputStream getOutputStream() {
  163. if (this.outputStream == null) {
  164. if (output != null) {
  165. try {
  166. setOutputStream(new PrintStream(
  167. new BufferedOutputStream(
  168. new FileOutputStream(output
  169. .getPath(),
  170. append))));
  171. } catch (IOException e) {
  172. throw new BuildException(e, getLocation());
  173. }
  174. } else {
  175. setOutputStream(new LogOutputStream(this, Project.MSG_INFO));
  176. }
  177. }
  178. return this.outputStream;
  179. }
  180. /**
  181. * sets a stream to which the stderr from the cvs exe should go
  182. * @param errorStream an output stream willing to process stderr
  183. */
  184. protected void setErrorStream(OutputStream errorStream) {
  185. this.errorStream = errorStream;
  186. }
  187. /**
  188. * access the stream to which the stderr from cvs should go
  189. * if this stream has already been set, it will be returned
  190. * if the stream has not yet been set, if the attribute error
  191. * has been set, the output stream will go to the file denoted by the error attribute
  192. * otherwise the stderr output will go to ant's logging system
  193. * @return output stream to which cvs' stderr should go to
  194. */
  195. protected OutputStream getErrorStream() {
  196. if (this.errorStream == null) {
  197. if (error != null) {
  198. try {
  199. setErrorStream(new PrintStream(
  200. new BufferedOutputStream(
  201. new FileOutputStream(error.getPath(),
  202. append))));
  203. } catch (IOException e) {
  204. throw new BuildException(e, getLocation());
  205. }
  206. } else {
  207. setErrorStream(new LogOutputStream(this, Project.MSG_WARN));
  208. }
  209. }
  210. return this.errorStream;
  211. }
  212. /**
  213. * Sets up the environment for toExecute and then runs it.
  214. * @param toExecute the command line to execute
  215. * @throws BuildException if failonError is set to true and the cvs command fails
  216. */
  217. protected void runCommand(Commandline toExecute) throws BuildException {
  218. // XXX: we should use JCVS (www.ice.com/JCVS) instead of
  219. // command line execution so that we don't rely on having
  220. // native CVS stuff around (SM)
  221. // We can't do it ourselves as jCVS is GPLed, a third party task
  222. // outside of jakarta repositories would be possible though (SB).
  223. Environment env = new Environment();
  224. if (port > 0) {
  225. Environment.Variable var = new Environment.Variable();
  226. var.setKey("CVS_CLIENT_PORT");
  227. var.setValue(String.valueOf(port));
  228. env.addVariable(var);
  229. }
  230. /**
  231. * Need a better cross platform integration with <cvspass>, so
  232. * use the same filename.
  233. */
  234. if (passFile == null) {
  235. File defaultPassFile = new File(
  236. System.getProperty("cygwin.user.home",
  237. System.getProperty("user.home"))
  238. + File.separatorChar + ".cvspass");
  239. if (defaultPassFile.exists()) {
  240. this.setPassfile(defaultPassFile);
  241. }
  242. }
  243. if (passFile != null) {
  244. if (passFile.isFile() && passFile.canRead()) {
  245. Environment.Variable var = new Environment.Variable();
  246. var.setKey("CVS_PASSFILE");
  247. var.setValue(String.valueOf(passFile));
  248. env.addVariable(var);
  249. log("Using cvs passfile: " + String.valueOf(passFile),
  250. Project.MSG_INFO);
  251. } else if (!passFile.canRead()) {
  252. log("cvs passfile: " + String.valueOf(passFile)
  253. + " ignored as it is not readable",
  254. Project.MSG_WARN);
  255. } else {
  256. log("cvs passfile: " + String.valueOf(passFile)
  257. + " ignored as it is not a file",
  258. Project.MSG_WARN);
  259. }
  260. }
  261. if (cvsRsh != null) {
  262. Environment.Variable var = new Environment.Variable();
  263. var.setKey("CVS_RSH");
  264. var.setValue(String.valueOf(cvsRsh));
  265. env.addVariable(var);
  266. }
  267. //
  268. // Just call the getExecuteStreamHandler() and let it handle
  269. // the semantics of instantiation or retrieval.
  270. //
  271. Execute exe = new Execute(getExecuteStreamHandler(), null);
  272. exe.setAntRun(getProject());
  273. if (dest == null) {
  274. dest = getProject().getBaseDir();
  275. }
  276. if (!dest.exists()) {
  277. dest.mkdirs();
  278. }
  279. exe.setWorkingDirectory(dest);
  280. exe.setCommandline(toExecute.getCommandline());
  281. exe.setEnvironment(env.getVariables());
  282. try {
  283. String actualCommandLine = executeToString(exe);
  284. log(actualCommandLine, Project.MSG_VERBOSE);
  285. int retCode = exe.execute();
  286. log("retCode=" + retCode, Project.MSG_DEBUG);
  287. /*Throw an exception if cvs exited with error. (Iulian)*/
  288. if (failOnError && Execute.isFailure(retCode)) {
  289. throw new BuildException("cvs exited with error code "
  290. + retCode
  291. + StringUtils.LINE_SEP
  292. + "Command line was ["
  293. + actualCommandLine + "]",
  294. getLocation());
  295. }
  296. } catch (IOException e) {
  297. if (failOnError) {
  298. throw new BuildException(e, getLocation());
  299. }
  300. log("Caught exception: " + e.getMessage(), Project.MSG_WARN);
  301. } catch (BuildException e) {
  302. if (failOnError) {
  303. throw(e);
  304. }
  305. Throwable t = e.getException();
  306. if (t == null) {
  307. t = e;
  308. }
  309. log("Caught exception: " + t.getMessage(), Project.MSG_WARN);
  310. } catch (Exception e) {
  311. if (failOnError) {
  312. throw new BuildException(e, getLocation());
  313. }
  314. log("Caught exception: " + e.getMessage(), Project.MSG_WARN);
  315. }
  316. }
  317. /**
  318. * do the work
  319. * @throws BuildException if failonerror is set to true and the
  320. * cvs command fails.
  321. */
  322. public void execute() throws BuildException {
  323. String savedCommand = getCommand();
  324. if (this.getCommand() == null && vecCommandlines.size() == 0) {
  325. // re-implement legacy behaviour:
  326. this.setCommand(AbstractCvsTask.DEFAULT_COMMAND);
  327. }
  328. String c = this.getCommand();
  329. Commandline cloned = null;
  330. if (c != null) {
  331. cloned = (Commandline) cmd.clone();
  332. cloned.createArgument(true).setLine(c);
  333. this.addConfiguredCommandline(cloned, true);
  334. }
  335. try {
  336. for (int i = 0; i < vecCommandlines.size(); i++) {
  337. this.runCommand((Commandline) vecCommandlines.elementAt(i));
  338. }
  339. } finally {
  340. if (cloned != null) {
  341. removeCommandline(cloned);
  342. }
  343. setCommand(savedCommand);
  344. FileUtils.close(outputStream);
  345. FileUtils.close(errorStream);
  346. }
  347. }
  348. private String executeToString(Execute execute) {
  349. StringBuffer stringBuffer =
  350. new StringBuffer(Commandline.describeCommand(execute
  351. .getCommandline()));
  352. String newLine = StringUtils.LINE_SEP;
  353. String[] variableArray = execute.getEnvironment();
  354. if (variableArray != null) {
  355. stringBuffer.append(newLine);
  356. stringBuffer.append(newLine);
  357. stringBuffer.append("environment:");
  358. stringBuffer.append(newLine);
  359. for (int z = 0; z < variableArray.length; z++) {
  360. stringBuffer.append(newLine);
  361. stringBuffer.append("\t");
  362. stringBuffer.append(variableArray[z]);
  363. }
  364. }
  365. return stringBuffer.toString();
  366. }
  367. /**
  368. * The CVSROOT variable.
  369. *
  370. * @param root the CVSROOT variable
  371. */
  372. public void setCvsRoot(String root) {
  373. // Check if not real cvsroot => set it to null
  374. if (root != null) {
  375. if (root.trim().equals("")) {
  376. root = null;
  377. }
  378. }
  379. this.cvsRoot = root;
  380. }
  381. /**
  382. * access the CVSROOT variable
  383. * @return CVSROOT
  384. */
  385. public String getCvsRoot() {
  386. return this.cvsRoot;
  387. }
  388. /**
  389. * The CVS_RSH variable.
  390. *
  391. * @param rsh the CVS_RSH variable
  392. */
  393. public void setCvsRsh(String rsh) {
  394. // Check if not real cvsrsh => set it to null
  395. if (rsh != null) {
  396. if (rsh.trim().equals("")) {
  397. rsh = null;
  398. }
  399. }
  400. this.cvsRsh = rsh;
  401. }
  402. /**
  403. * access the CVS_RSH variable
  404. * @return the CVS_RSH variable
  405. */
  406. public String getCvsRsh() {
  407. return this.cvsRsh;
  408. }
  409. /**
  410. * Port used by CVS to communicate with the server.
  411. *
  412. * @param port port of CVS
  413. */
  414. public void setPort(int port) {
  415. this.port = port;
  416. }
  417. /**
  418. * access the port of CVS
  419. * @return the port of CVS
  420. */
  421. public int getPort() {
  422. return this.port;
  423. }
  424. /**
  425. * Password file to read passwords from.
  426. *
  427. * @param passFile password file to read passwords from
  428. */
  429. public void setPassfile(File passFile) {
  430. this.passFile = passFile;
  431. }
  432. /**
  433. * find the password file
  434. * @return password file
  435. */
  436. public File getPassFile() {
  437. return this.passFile;
  438. }
  439. /**
  440. * The directory where the checked out files should be placed.
  441. *
  442. * <p>Note that this is different from CVS's -d command line
  443. * switch as Ant will never shorten pathnames to avoid empty
  444. * directories.</p>
  445. *
  446. * @param dest directory where the checked out files should be placed
  447. */
  448. public void setDest(File dest) {
  449. this.dest = dest;
  450. }
  451. /**
  452. * get the file where the checked out files should be placed
  453. *
  454. * @return directory where the checked out files should be placed
  455. */
  456. public File getDest() {
  457. return this.dest;
  458. }
  459. /**
  460. * The package/module to operate upon.
  461. *
  462. * @param p package or module to operate upon
  463. */
  464. public void setPackage(String p) {
  465. this.cvsPackage = p;
  466. }
  467. /**
  468. * access the package or module to operate upon
  469. *
  470. * @return package/module
  471. */
  472. public String getPackage() {
  473. return this.cvsPackage;
  474. }
  475. /**
  476. * tag or branch
  477. * @return tag or branch
  478. * @since ant 1.6.1
  479. */
  480. public String getTag() {
  481. return tag;
  482. }
  483. /**
  484. * The tag of the package/module to operate upon.
  485. * @param p tag
  486. */
  487. public void setTag(String p) {
  488. // Check if not real tag => set it to null
  489. if (p != null && p.trim().length() > 0) {
  490. tag = p;
  491. addCommandArgument("-r" + p);
  492. }
  493. }
  494. /**
  495. * This needs to be public to allow configuration
  496. * of commands externally.
  497. * @param arg command argument
  498. */
  499. public void addCommandArgument(String arg) {
  500. this.addCommandArgument(cmd, arg);
  501. }
  502. /**
  503. * This method adds a command line argument to an external command.
  504. *
  505. * I do not understand what this method does in this class ???
  506. * particularly not why it is public ????
  507. * AntoineLL July 23d 2003
  508. *
  509. * @param c command line to which one argument should be added
  510. * @param arg argument to add
  511. */
  512. public void addCommandArgument(Commandline c, String arg) {
  513. c.createArgument().setValue(arg);
  514. }
  515. /**
  516. * Use the most recent revision no later than the given date.
  517. * @param p a date as string in a format that the CVS executable
  518. * can understand see man cvs
  519. */
  520. public void setDate(String p) {
  521. if (p != null && p.trim().length() > 0) {
  522. addCommandArgument("-D");
  523. addCommandArgument(p);
  524. }
  525. }
  526. /**
  527. * The CVS command to execute.
  528. *
  529. * This should be deprecated, it is better to use the Commandline class ?
  530. * AntoineLL July 23d 2003
  531. *
  532. * @param c a command as string
  533. */
  534. public void setCommand(String c) {
  535. this.command = c;
  536. }
  537. /**
  538. * accessor to a command line as string
  539. *
  540. * This should be deprecated
  541. * AntoineLL July 23d 2003
  542. *
  543. * @return command line as string
  544. */
  545. public String getCommand() {
  546. return this.command;
  547. }
  548. /**
  549. * If true, suppress informational messages.
  550. * @param q if true, suppress informational messages
  551. */
  552. public void setQuiet(boolean q) {
  553. quiet = q;
  554. }
  555. /**
  556. * If true, suppress all messages.
  557. * @param q if true, suppress all messages
  558. * @since Ant 1.6
  559. */
  560. public void setReallyquiet(boolean q) {
  561. reallyquiet = q;
  562. }
  563. /**
  564. * If true, report only and don't change any files.
  565. *
  566. * @param ne if true, report only and do not change any files.
  567. */
  568. public void setNoexec(boolean ne) {
  569. noexec = ne;
  570. }
  571. /**
  572. * The file to direct standard output from the command.
  573. * @param output a file to which stdout should go
  574. */
  575. public void setOutput(File output) {
  576. this.output = output;
  577. }
  578. /**
  579. * The file to direct standard error from the command.
  580. *
  581. * @param error a file to which stderr should go
  582. */
  583. public void setError(File error) {
  584. this.error = error;
  585. }
  586. /**
  587. * Whether to append output/error when redirecting to a file.
  588. * @param value true indicated you want to append
  589. */
  590. public void setAppend(boolean value) {
  591. this.append = value;
  592. }
  593. /**
  594. * Stop the build process if the command exits with
  595. * a return code other than 0.
  596. * Defaults to false.
  597. * @param failOnError stop the build process if the command exits with
  598. * a return code other than 0
  599. */
  600. public void setFailOnError(boolean failOnError) {
  601. this.failOnError = failOnError;
  602. }
  603. /**
  604. * Configure a commandline element for things like cvsRoot, quiet, etc.
  605. * @param c the command line which will be configured
  606. * if the commandline is initially null, the function is a noop
  607. * otherwise the function append to the commandline arguments concerning
  608. * <ul>
  609. * <li>
  610. * cvs package
  611. * </li>
  612. * <li>
  613. * compression
  614. * </li>
  615. * <li>
  616. * quiet or reallyquiet
  617. * </li>
  618. * <li>cvsroot</li>
  619. * <li>noexec</li>
  620. * </ul>
  621. */
  622. protected void configureCommandline(Commandline c) {
  623. if (c == null) {
  624. return;
  625. }
  626. c.setExecutable("cvs");
  627. if (cvsPackage != null) {
  628. c.createArgument().setLine(cvsPackage);
  629. }
  630. if (this.compression > 0
  631. && this.compression <= MAXIMUM_COMRESSION_LEVEL) {
  632. c.createArgument(true).setValue("-z" + this.compression);
  633. }
  634. if (quiet && !reallyquiet) {
  635. c.createArgument(true).setValue("-q");
  636. }
  637. if (reallyquiet) {
  638. c.createArgument(true).setValue("-Q");
  639. }
  640. if (noexec) {
  641. c.createArgument(true).setValue("-n");
  642. }
  643. if (cvsRoot != null) {
  644. c.createArgument(true).setLine("-d" + cvsRoot);
  645. }
  646. }
  647. /**
  648. * remove a particular command from a vector of command lines
  649. * @param c command line which should be removed
  650. */
  651. protected void removeCommandline(Commandline c) {
  652. vecCommandlines.removeElement(c);
  653. }
  654. /**
  655. * Adds direct command-line to execute.
  656. * @param c command line to execute
  657. */
  658. public void addConfiguredCommandline(Commandline c) {
  659. this.addConfiguredCommandline(c, false);
  660. }
  661. /**
  662. * Configures and adds the given Commandline.
  663. * @param c commandline to insert
  664. * @param insertAtStart If true, c is
  665. * inserted at the beginning of the vector of command lines
  666. */
  667. public void addConfiguredCommandline(Commandline c,
  668. boolean insertAtStart) {
  669. if (c == null) {
  670. return;
  671. }
  672. this.configureCommandline(c);
  673. if (insertAtStart) {
  674. vecCommandlines.insertElementAt(c, 0);
  675. } else {
  676. vecCommandlines.addElement(c);
  677. }
  678. }
  679. /**
  680. * If set to a value 1-9 it adds -zN to the cvs command line, else
  681. * it disables compression.
  682. * @param level compression level 1 to 9
  683. */
  684. public void setCompressionLevel(int level) {
  685. this.compression = level;
  686. }
  687. /**
  688. * If true, this is the same as compressionlevel="3".
  689. *
  690. * @param usecomp If true, turns on compression using default
  691. * level, AbstractCvsTask.DEFAULT_COMPRESSION_LEVEL.
  692. */
  693. public void setCompression(boolean usecomp) {
  694. setCompressionLevel(usecomp
  695. ? AbstractCvsTask.DEFAULT_COMPRESSION_LEVEL : 0);
  696. }
  697. }