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.

ExecuteOn.java 24 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. /*
  2. * Copyright 2000-2004 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.File;
  19. import java.io.IOException;
  20. import java.util.Hashtable;
  21. import java.util.Vector;
  22. import org.apache.tools.ant.BuildException;
  23. import org.apache.tools.ant.DirectoryScanner;
  24. import org.apache.tools.ant.Project;
  25. import org.apache.tools.ant.types.Commandline;
  26. import org.apache.tools.ant.types.AbstractFileSet;
  27. import org.apache.tools.ant.types.DirSet;
  28. import org.apache.tools.ant.types.EnumeratedAttribute;
  29. import org.apache.tools.ant.types.FileList;
  30. import org.apache.tools.ant.types.FileSet;
  31. import org.apache.tools.ant.types.Mapper;
  32. import org.apache.tools.ant.util.FileNameMapper;
  33. import org.apache.tools.ant.util.SourceFileScanner;
  34. /**
  35. * Executes a given command, supplying a set of files as arguments.
  36. *
  37. * @since Ant 1.2
  38. *
  39. * @ant.task category="control" name="apply"
  40. */
  41. public class ExecuteOn extends ExecTask {
  42. protected Vector filesets = new Vector(); // contains AbstractFileSet
  43. // (both DirSet and FileSet)
  44. private Vector filelists = new Vector();
  45. private boolean relative = false;
  46. private boolean parallel = false;
  47. private boolean forwardSlash = false;
  48. protected String type = "file";
  49. protected Commandline.Marker srcFilePos = null;
  50. private boolean skipEmpty = false;
  51. protected Commandline.Marker targetFilePos = null;
  52. protected Mapper mapperElement = null;
  53. protected FileNameMapper mapper = null;
  54. protected File destDir = null;
  55. private int maxParallel = -1;
  56. private boolean addSourceFile = true;
  57. private boolean verbose = false;
  58. private boolean ignoreMissing = true;
  59. private boolean force = false;
  60. /**
  61. * Has <srcfile> been specified before <targetfile>
  62. */
  63. protected boolean srcIsFirst = true;
  64. /**
  65. * Source files to operate upon.
  66. */
  67. public void addFileset(FileSet set) {
  68. filesets.addElement(set);
  69. }
  70. /**
  71. * Adds directories to operate on.
  72. *
  73. * @param set the DirSet to add.
  74. *
  75. * @since Ant 1.6
  76. */
  77. public void addDirset(DirSet set) {
  78. filesets.addElement(set);
  79. }
  80. /**
  81. * Source files to operate upon.
  82. */
  83. public void addFilelist(FileList list) {
  84. filelists.addElement(list);
  85. }
  86. /**
  87. * Whether the filenames should be passed on the command line as
  88. * absolute or relative pathnames. Paths are relative to the base
  89. * directory of the corresponding fileset for source files or the
  90. * dest attribute for target files.
  91. */
  92. public void setRelative(boolean relative) {
  93. this.relative = relative;
  94. }
  95. /**
  96. * If true, run the command only once, appending all files as arguments.
  97. * If false, command will be executed once for every file. Defaults to false.
  98. */
  99. public void setParallel(boolean parallel) {
  100. this.parallel = parallel;
  101. }
  102. /**
  103. * Whether the command works only on files, directories or both?
  104. */
  105. public void setType(FileDirBoth type) {
  106. this.type = type.getValue();
  107. }
  108. /**
  109. * If no source files have been found or are newer than their
  110. * corresponding target files, do not run the command.
  111. */
  112. public void setSkipEmptyFilesets(boolean skip) {
  113. skipEmpty = skip;
  114. }
  115. /**
  116. * The directory where target files are to be placed.
  117. */
  118. public void setDest(File destDir) {
  119. this.destDir = destDir;
  120. }
  121. /**
  122. * The source and target file names on Windows and OS/2 must use
  123. * forward slash as file separator.
  124. */
  125. public void setForwardslash(boolean forwardSlash) {
  126. this.forwardSlash = forwardSlash;
  127. }
  128. /**
  129. * Limit the command line length by passing at maximum this many
  130. * sourcefiles at once to the command.
  131. *
  132. * <p>Set to &lt;= 0 for unlimited - this is the default.</p>
  133. *
  134. * @since Ant 1.6
  135. */
  136. public void setMaxParallel(int max) {
  137. maxParallel = max;
  138. }
  139. /**
  140. * Whether to send the source file name on the command line.
  141. *
  142. * <p>Defaults to <code>true</code>.
  143. *
  144. * @since Ant 1.6
  145. */
  146. public void setAddsourcefile(boolean b) {
  147. addSourceFile = b;
  148. }
  149. /**
  150. * Whether to print a verbose summary after execution.
  151. *
  152. * @since Ant 1.6
  153. */
  154. public void setVerbose(boolean b) {
  155. verbose = b;
  156. }
  157. /**
  158. * Whether to ignore nonexistent files from filelists.
  159. *
  160. * @since Ant 1.6.2
  161. */
  162. public void setIgnoremissing(boolean b) {
  163. ignoreMissing = b;
  164. }
  165. /**
  166. * Whether to bypass timestamp comparisons for target files.
  167. *
  168. * @since Ant 1.7
  169. */
  170. public void setForce(boolean b) {
  171. force = b;
  172. }
  173. /**
  174. * Marker that indicates where the name of the source file should
  175. * be put on the command line.
  176. */
  177. public Commandline.Marker createSrcfile() {
  178. if (srcFilePos != null) {
  179. throw new BuildException(getTaskType() + " doesn\'t support multiple "
  180. + "srcfile elements.", getLocation());
  181. }
  182. srcFilePos = cmdl.createMarker();
  183. return srcFilePos;
  184. }
  185. /**
  186. * Marker that indicates where the name of the target file should
  187. * be put on the command line.
  188. */
  189. public Commandline.Marker createTargetfile() {
  190. if (targetFilePos != null) {
  191. throw new BuildException(getTaskType() + " doesn\'t support multiple "
  192. + "targetfile elements.", getLocation());
  193. }
  194. targetFilePos = cmdl.createMarker();
  195. srcIsFirst = (srcFilePos != null);
  196. return targetFilePos;
  197. }
  198. /**
  199. * Mapper to use for mapping source files to target files.
  200. */
  201. public Mapper createMapper() throws BuildException {
  202. if (mapperElement != null) {
  203. throw new BuildException("Cannot define more than one mapper",
  204. getLocation());
  205. }
  206. mapperElement = new Mapper(getProject());
  207. return mapperElement;
  208. }
  209. /**
  210. * A nested filenamemapper
  211. * @param fileNameMapper the mapper to add
  212. * @since Ant 1.6.3
  213. */
  214. public void add(FileNameMapper fileNameMapper) {
  215. createMapper().add(fileNameMapper);
  216. }
  217. /**
  218. * @todo using taskName here is brittle, as a user could override it.
  219. * this should probably be modified to use the classname instead.
  220. */
  221. protected void checkConfiguration() {
  222. if ("execon".equals(getTaskName())) {
  223. log("!! execon is deprecated. Use apply instead. !!");
  224. }
  225. super.checkConfiguration();
  226. if (filesets.size() == 0 && filelists.size() == 0) {
  227. throw new BuildException("no filesets and no filelists specified",
  228. getLocation());
  229. }
  230. if (targetFilePos != null || mapperElement != null
  231. || destDir != null) {
  232. if (mapperElement == null) {
  233. throw new BuildException("no mapper specified", getLocation());
  234. }
  235. if (destDir == null) {
  236. throw new BuildException("no dest attribute specified",
  237. getLocation());
  238. }
  239. mapper = mapperElement.getImplementation();
  240. }
  241. }
  242. protected ExecuteStreamHandler createHandler() throws BuildException {
  243. //if we have a RedirectorElement, return a decoy
  244. return (redirectorElement == null)
  245. ? super.createHandler() : new PumpStreamHandler();
  246. }
  247. protected void setupRedirector() {
  248. super.setupRedirector();
  249. redirector.setAppendProperties(true);
  250. }
  251. protected void runExec(Execute exe) throws BuildException {
  252. int totalFiles = 0;
  253. int totalDirs = 0;
  254. boolean haveExecuted = false;
  255. try {
  256. Vector fileNames = new Vector();
  257. Vector baseDirs = new Vector();
  258. for (int i = 0; i < filesets.size(); i++) {
  259. String currentType = type;
  260. AbstractFileSet fs = (AbstractFileSet) filesets.elementAt(i);
  261. if (fs instanceof DirSet) {
  262. if (!"dir".equals(type)) {
  263. log("Found a nested dirset but type is " + type + ". "
  264. + "Temporarily switching to type=\"dir\" on the"
  265. + " assumption that you really did mean"
  266. + " <dirset> not <fileset>.", Project.MSG_DEBUG);
  267. currentType = "dir";
  268. }
  269. }
  270. File base = fs.getDir(getProject());
  271. DirectoryScanner ds = fs.getDirectoryScanner(getProject());
  272. if (!"dir".equals(currentType)) {
  273. String[] s = getFiles(base, ds);
  274. for (int j = 0; j < s.length; j++) {
  275. totalFiles++;
  276. fileNames.addElement(s[j]);
  277. baseDirs.addElement(base);
  278. }
  279. }
  280. if (!"file".equals(currentType)) {
  281. String[] s = getDirs(base, ds);
  282. for (int j = 0; j < s.length; j++) {
  283. totalDirs++;
  284. fileNames.addElement(s[j]);
  285. baseDirs.addElement(base);
  286. }
  287. }
  288. if (fileNames.size() == 0 && skipEmpty) {
  289. int includedCount
  290. = ((!"dir".equals(currentType))
  291. ? ds.getIncludedFilesCount() : 0)
  292. + ((!"file".equals(currentType))
  293. ? ds.getIncludedDirsCount() : 0);
  294. log("Skipping fileset for directory " + base + ". It is "
  295. + ((includedCount > 0) ? "up to date." : "empty."),
  296. Project.MSG_INFO);
  297. continue;
  298. }
  299. if (!parallel) {
  300. String[] s = new String[fileNames.size()];
  301. fileNames.copyInto(s);
  302. for (int j = 0; j < s.length; j++) {
  303. String[] command = getCommandline(s[j], base);
  304. log(Commandline.describeCommand(command),
  305. Project.MSG_VERBOSE);
  306. exe.setCommandline(command);
  307. if (redirectorElement != null) {
  308. setupRedirector();
  309. redirectorElement.configure(redirector, s[j]);
  310. }
  311. if (redirectorElement != null || haveExecuted) {
  312. // need to reset the stream handler to restart
  313. // reading of pipes;
  314. // go ahead and do it always w/ nested redirectors
  315. exe.setStreamHandler(redirector.createHandler());
  316. }
  317. runExecute(exe);
  318. haveExecuted = true;
  319. }
  320. fileNames.removeAllElements();
  321. baseDirs.removeAllElements();
  322. }
  323. }
  324. for (int i = 0; i < filelists.size(); i++) {
  325. FileList list = (FileList) filelists.elementAt(i);
  326. File base = list.getDir(getProject());
  327. String[] names = getFilesAndDirs(list);
  328. for (int j = 0; j < names.length; j++) {
  329. File f = new File(base, names[j]);
  330. if ((!ignoreMissing) || (f.isFile() && !"dir".equals(type))
  331. || (f.isDirectory() && !"file".equals(type))) {
  332. if (ignoreMissing || f.isFile()) {
  333. totalFiles++;
  334. } else {
  335. totalDirs++;
  336. }
  337. fileNames.addElement(names[j]);
  338. baseDirs.addElement(base);
  339. }
  340. }
  341. if (fileNames.size() == 0 && skipEmpty) {
  342. DirectoryScanner ds = new DirectoryScanner();
  343. ds.setBasedir(base);
  344. ds.setIncludes(list.getFiles(getProject()));
  345. ds.scan();
  346. int includedCount
  347. = ds.getIncludedFilesCount() + ds.getIncludedDirsCount();
  348. log("Skipping filelist for directory " + base + ". It is "
  349. + ((includedCount > 0) ? "up to date." : "empty."),
  350. Project.MSG_INFO);
  351. continue;
  352. }
  353. if (!parallel) {
  354. String[] s = new String[fileNames.size()];
  355. fileNames.copyInto(s);
  356. for (int j = 0; j < s.length; j++) {
  357. String[] command = getCommandline(s[j], base);
  358. log(Commandline.describeCommand(command),
  359. Project.MSG_VERBOSE);
  360. exe.setCommandline(command);
  361. if (redirectorElement != null) {
  362. setupRedirector();
  363. redirectorElement.configure(redirector, s[j]);
  364. }
  365. if (redirectorElement != null || haveExecuted) {
  366. // need to reset the stream handler to restart
  367. // reading of pipes;
  368. // go ahead and do it always w/ nested redirectors
  369. exe.setStreamHandler(redirector.createHandler());
  370. }
  371. runExecute(exe);
  372. haveExecuted = true;
  373. }
  374. fileNames.removeAllElements();
  375. baseDirs.removeAllElements();
  376. }
  377. }
  378. if (parallel && (fileNames.size() > 0 || !skipEmpty)) {
  379. runParallel(exe, fileNames, baseDirs);
  380. haveExecuted = true;
  381. }
  382. if (haveExecuted) {
  383. log("Applied " + cmdl.getExecutable() + " to "
  384. + totalFiles + " file"
  385. + (totalFiles != 1 ? "s" : "") + " and "
  386. + totalDirs + " director"
  387. + (totalDirs != 1 ? "ies" : "y") + ".",
  388. verbose ? Project.MSG_INFO : Project.MSG_VERBOSE);
  389. }
  390. } catch (IOException e) {
  391. throw new BuildException("Execute failed: " + e, e, getLocation());
  392. } finally {
  393. // close the output file if required
  394. logFlush();
  395. redirector.setAppendProperties(false);
  396. redirector.setProperties();
  397. }
  398. }
  399. /**
  400. * Construct the command line for parallel execution.
  401. *
  402. * @param srcFiles The filenames to add to the commandline
  403. * @param baseDirs filenames are relative to this dir
  404. */
  405. protected String[] getCommandline(String[] srcFiles, File[] baseDirs) {
  406. final char fileSeparator = File.separatorChar;
  407. Vector targets = new Vector();
  408. if (targetFilePos != null) {
  409. Hashtable addedFiles = new Hashtable();
  410. for (int i = 0; i < srcFiles.length; i++) {
  411. String[] subTargets = mapper.mapFileName(srcFiles[i]);
  412. if (subTargets != null) {
  413. for (int j = 0; j < subTargets.length; j++) {
  414. String name = null;
  415. if (!relative) {
  416. name = (new File(destDir, subTargets[j])).getAbsolutePath();
  417. } else {
  418. name = subTargets[j];
  419. }
  420. if (forwardSlash && fileSeparator != '/') {
  421. name = name.replace(fileSeparator, '/');
  422. }
  423. if (!addedFiles.contains(name)) {
  424. targets.addElement(name);
  425. addedFiles.put(name, name);
  426. }
  427. }
  428. }
  429. }
  430. }
  431. String[] targetFiles = new String[targets.size()];
  432. targets.copyInto(targetFiles);
  433. if (!addSourceFile) {
  434. srcFiles = new String[0];
  435. }
  436. String[] orig = cmdl.getCommandline();
  437. String[] result
  438. = new String[orig.length + srcFiles.length + targetFiles.length];
  439. int srcIndex = orig.length;
  440. if (srcFilePos != null) {
  441. srcIndex = srcFilePos.getPosition();
  442. }
  443. if (targetFilePos != null) {
  444. int targetIndex = targetFilePos.getPosition();
  445. if (srcIndex < targetIndex
  446. || (srcIndex == targetIndex && srcIsFirst)) {
  447. // 0 --> srcIndex
  448. System.arraycopy(orig, 0, result, 0, srcIndex);
  449. // srcIndex --> targetIndex
  450. System.arraycopy(orig, srcIndex, result,
  451. srcIndex + srcFiles.length,
  452. targetIndex - srcIndex);
  453. // targets are already absolute file names
  454. System.arraycopy(targetFiles, 0, result,
  455. targetIndex + srcFiles.length,
  456. targetFiles.length);
  457. // targetIndex --> end
  458. System.arraycopy(orig, targetIndex, result,
  459. targetIndex + srcFiles.length + targetFiles.length,
  460. orig.length - targetIndex);
  461. } else {
  462. // 0 --> targetIndex
  463. System.arraycopy(orig, 0, result, 0, targetIndex);
  464. // targets are already absolute file names
  465. System.arraycopy(targetFiles, 0, result,
  466. targetIndex,
  467. targetFiles.length);
  468. // targetIndex --> srcIndex
  469. System.arraycopy(orig, targetIndex, result,
  470. targetIndex + targetFiles.length,
  471. srcIndex - targetIndex);
  472. // srcIndex --> end
  473. System.arraycopy(orig, srcIndex, result,
  474. srcIndex + srcFiles.length + targetFiles.length,
  475. orig.length - srcIndex);
  476. srcIndex += targetFiles.length;
  477. }
  478. } else { // no targetFilePos
  479. // 0 --> srcIndex
  480. System.arraycopy(orig, 0, result, 0, srcIndex);
  481. // srcIndex --> end
  482. System.arraycopy(orig, srcIndex, result,
  483. srcIndex + srcFiles.length,
  484. orig.length - srcIndex);
  485. }
  486. // fill in source file names
  487. for (int i = 0; i < srcFiles.length; i++) {
  488. if (!relative) {
  489. result[srcIndex + i] =
  490. (new File(baseDirs[i], srcFiles[i])).getAbsolutePath();
  491. } else {
  492. result[srcIndex + i] = srcFiles[i];
  493. }
  494. if (forwardSlash && fileSeparator != '/') {
  495. result[srcIndex + i] =
  496. result[srcIndex + i].replace(fileSeparator, '/');
  497. }
  498. }
  499. return result;
  500. }
  501. /**
  502. * Construct the command line for serial execution.
  503. *
  504. * @param srcFile The filename to add to the commandline
  505. * @param baseDir filename is relative to this dir
  506. */
  507. protected String[] getCommandline(String srcFile, File baseDir) {
  508. return getCommandline(new String[] {srcFile}, new File[] {baseDir});
  509. }
  510. /**
  511. * Return the list of files from this DirectoryScanner that should
  512. * be included on the command line.
  513. */
  514. protected String[] getFiles(File baseDir, DirectoryScanner ds) {
  515. return restrict(ds.getIncludedFiles(), baseDir);
  516. }
  517. /**
  518. * Return the list of Directories from this DirectoryScanner that
  519. * should be included on the command line.
  520. */
  521. protected String[] getDirs(File baseDir, DirectoryScanner ds) {
  522. return restrict(ds.getIncludedDirectories(), baseDir);
  523. }
  524. /**
  525. * Return the list of files or directories from this FileList that
  526. * should be included on the command line.
  527. *
  528. * @since Ant 1.6.2
  529. */
  530. protected String[] getFilesAndDirs(FileList list) {
  531. return restrict(list.getFiles(getProject()), list.getDir(getProject()));
  532. }
  533. private String[] restrict(String[] s, File baseDir) {
  534. return (mapper == null || force) ? s
  535. : new SourceFileScanner(this).restrict(s, baseDir, destDir, mapper);
  536. }
  537. /**
  538. * Runs the command in "parallel" mode, making sure that at most
  539. * maxParallel sourcefiles get passed on the command line.
  540. *
  541. * @since Ant 1.6
  542. */
  543. protected void runParallel(Execute exe, Vector fileNames,
  544. Vector baseDirs)
  545. throws IOException, BuildException {
  546. String[] s = new String[fileNames.size()];
  547. fileNames.copyInto(s);
  548. File[] b = new File[baseDirs.size()];
  549. baseDirs.copyInto(b);
  550. if (maxParallel <= 0
  551. || s.length == 0 /* this is skipEmpty == false */) {
  552. String[] command = getCommandline(s, b);
  553. log(Commandline.describeCommand(command), Project.MSG_VERBOSE);
  554. exe.setCommandline(command);
  555. runExecute(exe);
  556. } else {
  557. int stillToDo = fileNames.size();
  558. int currentOffset = 0;
  559. while (stillToDo > 0) {
  560. int currentAmount = Math.min(stillToDo, maxParallel);
  561. String[] cs = new String[currentAmount];
  562. System.arraycopy(s, currentOffset, cs, 0, currentAmount);
  563. File[] cb = new File[currentAmount];
  564. System.arraycopy(b, currentOffset, cb, 0, currentAmount);
  565. String[] command = getCommandline(cs, cb);
  566. log(Commandline.describeCommand(command), Project.MSG_VERBOSE);
  567. exe.setCommandline(command);
  568. if (redirectorElement != null) {
  569. setupRedirector();
  570. redirectorElement.configure(redirector, null);
  571. }
  572. if (redirectorElement != null || currentOffset > 0) {
  573. // need to reset the stream handler to restart
  574. // reading of pipes;
  575. // go ahead and do it always w/ nested redirectors
  576. exe.setStreamHandler(redirector.createHandler());
  577. }
  578. runExecute(exe);
  579. stillToDo -= currentAmount;
  580. currentOffset += currentAmount;
  581. }
  582. }
  583. }
  584. /**
  585. * Enumerated attribute with the values "file", "dir" and "both"
  586. * for the type attribute.
  587. */
  588. public static class FileDirBoth extends EnumeratedAttribute {
  589. /**
  590. * @see EnumeratedAttribute#getValues
  591. */
  592. public String[] getValues() {
  593. return new String[] {"file", "dir", "both"};
  594. }
  595. }
  596. }