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

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