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.

Target.java 17 kB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  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;
  19. import java.util.ArrayList;
  20. import java.util.Collections;
  21. import java.util.Enumeration;
  22. import java.util.Hashtable;
  23. import java.util.List;
  24. import java.util.StringTokenizer;
  25. import org.apache.tools.ant.property.LocalProperties;
  26. import org.apache.tools.ant.taskdefs.condition.And;
  27. import org.apache.tools.ant.taskdefs.condition.Condition;
  28. import org.apache.tools.ant.taskdefs.condition.Or;
  29. /**
  30. * Class to implement a target object with required parameters.
  31. *
  32. * <p>If you are creating Targets programmatically, make sure you set
  33. * the Location to a useful value. In particular all targets should
  34. * have different location values.</p>
  35. */
  36. public class Target implements TaskContainer {
  37. /** Name of this target. */
  38. private String name;
  39. /** The "if" condition to test on execution. */
  40. private String ifString = "";
  41. /** The "unless" condition to test on execution. */
  42. private String unlessString = "";
  43. private Condition ifCondition;
  44. private Condition unlessCondition;
  45. /** List of targets this target is dependent on. */
  46. private List<String> dependencies = null;
  47. /** Children of this target (tasks and data types). */
  48. private List<Object> children = new ArrayList<>();
  49. /** Since Ant 1.6.2 */
  50. private Location location = Location.UNKNOWN_LOCATION;
  51. /** Project this target belongs to. */
  52. private Project project;
  53. /** Description of this target, if any. */
  54. private String description = null;
  55. /** Default constructor. */
  56. public Target() {
  57. //empty
  58. }
  59. /**
  60. * Cloning constructor.
  61. * @param other the Target to clone.
  62. */
  63. public Target(Target other) {
  64. this.name = other.name;
  65. this.ifString = other.ifString;
  66. this.unlessString = other.unlessString;
  67. this.ifCondition = other.ifCondition;
  68. this.unlessCondition = other.unlessCondition;
  69. this.dependencies = other.dependencies;
  70. this.location = other.location;
  71. this.project = other.project;
  72. this.description = other.description;
  73. // The children are added to after this cloning
  74. this.children = other.children;
  75. }
  76. /**
  77. * Sets the project this target belongs to.
  78. *
  79. * @param project The project this target belongs to.
  80. * Must not be <code>null</code>.
  81. */
  82. public void setProject(Project project) {
  83. this.project = project;
  84. }
  85. /**
  86. * Returns the project this target belongs to.
  87. *
  88. * @return The project this target belongs to, or <code>null</code> if
  89. * the project has not been set yet.
  90. */
  91. public Project getProject() {
  92. return project;
  93. }
  94. /**
  95. * Sets the location of this target's definition.
  96. *
  97. * @param location <code>Location</code>
  98. * @since 1.6.2
  99. */
  100. public void setLocation(Location location) {
  101. this.location = location;
  102. }
  103. /**
  104. * Get the location of this target's definition.
  105. *
  106. * @return <code>Location</code>
  107. * @since 1.6.2
  108. */
  109. public Location getLocation() {
  110. return location;
  111. }
  112. /**
  113. * Sets the list of targets this target is dependent on.
  114. * The targets themselves are not resolved at this time.
  115. *
  116. * @param depS A comma-separated list of targets this target
  117. * depends on. Must not be <code>null</code>.
  118. */
  119. public void setDepends(String depS) {
  120. for (String dep : parseDepends(depS, getName(), "depends")) {
  121. addDependency(dep);
  122. }
  123. }
  124. public static List<String> parseDepends(String depends,
  125. String targetName,
  126. String attributeName) {
  127. List<String> list = new ArrayList<>();
  128. if (depends.length() > 0) {
  129. StringTokenizer tok =
  130. new StringTokenizer(depends, ",", true);
  131. while (tok.hasMoreTokens()) {
  132. String token = tok.nextToken().trim();
  133. // Make sure the dependency is not empty string
  134. if ("".equals(token) || ",".equals(token)) {
  135. throw new BuildException("Syntax Error: "
  136. + attributeName
  137. + " attribute of target \""
  138. + targetName
  139. + "\" contains an empty string.");
  140. }
  141. list.add(token);
  142. // Make sure that depends attribute does not
  143. // end in a ,
  144. if (tok.hasMoreTokens()) {
  145. token = tok.nextToken();
  146. if (!tok.hasMoreTokens() || !",".equals(token)) {
  147. throw new BuildException("Syntax Error: "
  148. + attributeName
  149. + " attribute for target \""
  150. + targetName
  151. + "\" ends with a \",\" "
  152. + "character");
  153. }
  154. }
  155. }
  156. }
  157. return list;
  158. }
  159. /**
  160. * Sets the name of this target.
  161. *
  162. * @param name The name of this target. Should not be <code>null</code>.
  163. */
  164. public void setName(String name) {
  165. this.name = name;
  166. }
  167. /**
  168. * Returns the name of this target.
  169. *
  170. * @return the name of this target, or <code>null</code> if the
  171. * name has not been set yet.
  172. */
  173. public String getName() {
  174. return name;
  175. }
  176. /**
  177. * Adds a task to this target.
  178. *
  179. * @param task The task to be added. Must not be <code>null</code>.
  180. */
  181. public void addTask(Task task) {
  182. children.add(task);
  183. }
  184. /**
  185. * Adds the wrapper for a data type element to this target.
  186. *
  187. * @param r The wrapper for the data type element to be added.
  188. * Must not be <code>null</code>.
  189. */
  190. public void addDataType(RuntimeConfigurable r) {
  191. children.add(r);
  192. }
  193. /**
  194. * Returns the current set of tasks to be executed by this target.
  195. *
  196. * @return an array of the tasks currently within this target
  197. */
  198. public Task[] getTasks() {
  199. List<Task> tasks = new ArrayList<>(children.size());
  200. for (Object o : children) {
  201. if (o instanceof Task) {
  202. tasks.add((Task) o);
  203. }
  204. }
  205. return tasks.toArray(new Task[tasks.size()]);
  206. }
  207. /**
  208. * Adds a dependency to this target.
  209. *
  210. * @param dependency The name of a target this target is dependent on.
  211. * Must not be <code>null</code>.
  212. */
  213. public void addDependency(String dependency) {
  214. if (dependencies == null) {
  215. dependencies = new ArrayList<>(2);
  216. }
  217. dependencies.add(dependency);
  218. }
  219. /**
  220. * Returns an enumeration of the dependencies of this target.
  221. *
  222. * @return an enumeration of the dependencies of this target (enumeration of String)
  223. */
  224. public Enumeration<String> getDependencies() {
  225. return dependencies == null ? Collections.emptyEnumeration()
  226. : Collections.enumeration(dependencies);
  227. }
  228. /**
  229. * Does this target depend on the named target?
  230. * @param other the other named target.
  231. * @return true if the target does depend on the named target
  232. * @since Ant 1.6
  233. */
  234. public boolean dependsOn(String other) {
  235. Project p = getProject();
  236. Hashtable<String, Target> t = p == null ? null : p.getTargets();
  237. return p != null && p.topoSort(getName(), t, false).contains(t.get(other));
  238. }
  239. /**
  240. * Sets the "if" condition to test on execution. This is the
  241. * name of a property to test for existence - if the property
  242. * is not set, the task will not execute. The property goes
  243. * through property substitution once before testing, so if
  244. * property <code>foo</code> has value <code>bar</code>, setting
  245. * the "if" condition to <code>${foo}_x</code> will mean that the
  246. * task will only execute if property <code>bar_x</code> is set.
  247. *
  248. * @param property The property condition to test on execution.
  249. * May be <code>null</code>, in which case
  250. * no "if" test is performed.
  251. */
  252. public void setIf(String property) {
  253. ifString = property == null ? "" : property;
  254. setIf(() -> {
  255. PropertyHelper propertyHelper =
  256. PropertyHelper.getPropertyHelper(getProject());
  257. Object o = propertyHelper.parseProperties(ifString);
  258. return propertyHelper.testIfCondition(o);
  259. });
  260. }
  261. /**
  262. * Returns the "if" property condition of this target.
  263. *
  264. * @return the "if" property condition or <code>null</code> if no
  265. * "if" condition had been defined.
  266. * @since 1.6.2
  267. */
  268. public String getIf() {
  269. return "".equals(ifString) ? null : ifString;
  270. }
  271. /**
  272. * Same as {@link #setIf(String)} but requires a {@link Condition} instance
  273. *
  274. * @param condition Condition
  275. * @since 1.9
  276. */
  277. public void setIf(Condition condition) {
  278. if (ifCondition == null) {
  279. ifCondition = condition;
  280. } else {
  281. And andCondition = new And();
  282. andCondition.setProject(getProject());
  283. andCondition.setLocation(getLocation());
  284. andCondition.add(ifCondition);
  285. andCondition.add(condition);
  286. ifCondition = andCondition;
  287. }
  288. }
  289. /**
  290. * Sets the "unless" condition to test on execution. This is the
  291. * name of a property to test for existence - if the property
  292. * is set, the task will not execute. The property goes
  293. * through property substitution once before testing, so if
  294. * property <code>foo</code> has value <code>bar</code>, setting
  295. * the "unless" condition to <code>${foo}_x</code> will mean that the
  296. * task will only execute if property <code>bar_x</code> isn't set.
  297. *
  298. * @param property The property condition to test on execution.
  299. * May be <code>null</code>, in which case
  300. * no "unless" test is performed.
  301. */
  302. public void setUnless(String property) {
  303. unlessString = property == null ? "" : property;
  304. setUnless(() -> {
  305. PropertyHelper propertyHelper =
  306. PropertyHelper.getPropertyHelper(getProject());
  307. Object o = propertyHelper.parseProperties(unlessString);
  308. return !propertyHelper.testUnlessCondition(o);
  309. });
  310. }
  311. /**
  312. * Returns the "unless" property condition of this target.
  313. *
  314. * @return the "unless" property condition or <code>null</code>
  315. * if no "unless" condition had been defined.
  316. * @since 1.6.2
  317. */
  318. public String getUnless() {
  319. return "".equals(unlessString) ? null : unlessString;
  320. }
  321. /**
  322. * Same as {@link #setUnless(String)} but requires a {@link Condition} instance
  323. *
  324. * @param condition Condition
  325. * @since 1.9
  326. */
  327. public void setUnless(Condition condition) {
  328. if (unlessCondition == null) {
  329. unlessCondition = condition;
  330. } else {
  331. Or orCondition = new Or();
  332. orCondition.setProject(getProject());
  333. orCondition.setLocation(getLocation());
  334. orCondition.add(unlessCondition);
  335. orCondition.add(condition);
  336. unlessCondition = orCondition;
  337. }
  338. }
  339. /**
  340. * Sets the description of this target.
  341. *
  342. * @param description The description for this target.
  343. * May be <code>null</code>, indicating that no
  344. * description is available.
  345. */
  346. public void setDescription(String description) {
  347. this.description = description;
  348. }
  349. /**
  350. * Returns the description of this target.
  351. *
  352. * @return the description of this target, or <code>null</code> if no
  353. * description is available.
  354. */
  355. public String getDescription() {
  356. return description;
  357. }
  358. /**
  359. * Returns the name of this target.
  360. *
  361. * @return the name of this target, or <code>null</code> if the
  362. * name has not been set yet.
  363. */
  364. @Override
  365. public String toString() {
  366. return name;
  367. }
  368. /**
  369. * Executes the target if the "if" and "unless" conditions are
  370. * satisfied. Dependency checking should be done before calling this
  371. * method, as it does no checking of its own. If either the "if"
  372. * or "unless" test prevents this target from being executed, a verbose
  373. * message is logged giving the reason. It is recommended that clients
  374. * of this class call performTasks rather than this method so that
  375. * appropriate build events are fired.
  376. *
  377. * @exception BuildException if any of the tasks fail or if a data type
  378. * configuration fails.
  379. *
  380. * @see #performTasks()
  381. * @see #setIf(String)
  382. * @see #setUnless(String)
  383. */
  384. public void execute() throws BuildException {
  385. if (ifCondition != null && !ifCondition.eval()) {
  386. project.log(this, "Skipped because property '" + project.replaceProperties(ifString)
  387. + "' not set.", Project.MSG_VERBOSE);
  388. return;
  389. }
  390. if (unlessCondition != null && unlessCondition.eval()) {
  391. project.log(this, "Skipped because property '"
  392. + project.replaceProperties(unlessString) + "' set.", Project.MSG_VERBOSE);
  393. return;
  394. }
  395. LocalProperties localProperties = LocalProperties.get(getProject());
  396. localProperties.enterScope();
  397. try {
  398. // use index-based approach to avoid ConcurrentModificationExceptions;
  399. // also account for growing target children
  400. // do not optimize this loop by replacing children.size() by a variable
  401. // as children can be added dynamically as in RhinoScriptTest where a target is adding work for itself
  402. for (int i = 0; i < children.size(); i++) {
  403. Object o = children.get(i);
  404. if (o instanceof Task) {
  405. Task task = (Task) o;
  406. task.perform();
  407. } else {
  408. ((RuntimeConfigurable) o).maybeConfigure(project);
  409. }
  410. }
  411. } finally {
  412. localProperties.exitScope();
  413. }
  414. }
  415. /**
  416. * Performs the tasks within this target (if the conditions are met),
  417. * firing target started/target finished messages around a call to
  418. * execute.
  419. *
  420. * @see #execute()
  421. */
  422. public final void performTasks() {
  423. RuntimeException thrown = null;
  424. project.fireTargetStarted(this);
  425. try {
  426. execute();
  427. } catch (RuntimeException exc) {
  428. thrown = exc;
  429. throw exc;
  430. } finally {
  431. project.fireTargetFinished(this, thrown);
  432. }
  433. }
  434. /**
  435. * Replaces all occurrences of the given task in the list
  436. * of children with the replacement data type wrapper.
  437. *
  438. * @param el The task to replace.
  439. * Must not be <code>null</code>.
  440. * @param o The data type wrapper to replace <code>el</code> with.
  441. */
  442. void replaceChild(Task el, RuntimeConfigurable o) {
  443. int index;
  444. while ((index = children.indexOf(el)) >= 0) {
  445. children.set(index, o);
  446. }
  447. }
  448. /**
  449. * Replaces all occurrences of the given task in the list
  450. * of children with the replacement task.
  451. *
  452. * @param el The task to replace.
  453. * Must not be <code>null</code>.
  454. * @param o The task to replace <code>el</code> with.
  455. */
  456. void replaceChild(Task el, Task o) {
  457. int index;
  458. while ((index = children.indexOf(el)) >= 0) {
  459. children.set(index, o);
  460. }
  461. }
  462. }