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
19 package org.apache.tools.ant.taskdefs;
20
21 import java.io.File;
22 import java.util.Iterator;
23 import org.apache.tools.ant.BuildException;
24 import org.apache.tools.ant.Project;
25 import org.apache.tools.ant.types.Path;
26 import org.apache.tools.ant.types.FileSet;
27 import org.apache.tools.ant.types.FileList;
28 import org.apache.tools.ant.types.Resource;
29 import org.apache.tools.ant.types.TimeComparison;
30 import org.apache.tools.ant.types.ResourceCollection;
31 import org.apache.tools.ant.types.resources.Union;
32 import org.apache.tools.ant.types.resources.Restrict;
33 import org.apache.tools.ant.types.resources.Resources;
34 import org.apache.tools.ant.types.resources.FileResource;
35 import org.apache.tools.ant.types.resources.selectors.Not;
36 import org.apache.tools.ant.types.resources.selectors.Exists;
37 import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
38 import org.apache.tools.ant.types.resources.comparators.Reverse;
39 import org.apache.tools.ant.types.resources.comparators.ResourceComparator;
40
41 /**
42 * Examines and removes out of date target files. If any of the target files
43 * are out of date with respect to any of the source files, all target
44 * files are removed. This is useful where dependencies cannot be
45 * computed (for example, dynamically interpreted parameters or files
46 * that need to stay in synch but are not directly linked) or where
47 * the ant task in question could compute them but does not (for
48 * example, the linked DTD for an XML file using the XSLT task).
49 *
50 * nested arguments:
51 * <ul>
52 * <li>sources (resource union describing the source resources to examine)
53 * <li>srcfileset (fileset describing the source files to examine)
54 * <li>srcfilelist (filelist describing the source files to examine)
55 * <li>targets (path describing the target files to examine)
56 * <li>targetfileset (fileset describing the target files to examine)
57 * <li>targetfilelist (filelist describing the target files to examine)
58 * </ul>
59 * At least one of both source and target entities is required.
60 * <p>
61 * This task will examine each of the sources against each of the target files. If
62 * any target files are out of date with respect to any of the sources, all targets
63 * are removed. If any sources or targets do not exist, all targets are removed.
64 * Hint: If missing files should be ignored, specify them as include patterns
65 * in filesets, rather than using filelists.
66 * </p><p>
67 * This task attempts to optimize speed of dependency checking
68 * by comparing only the dates of the oldest target file and the newest source.
69 * </p><p>
70 * Example uses:
71 * <ul><li>
72 * Record the fact that an XML file must be up to date with respect to its XSD
73 * (Schema file), even though the XML file itself includes no reference to its XSD.
74 * </li><li>
75 * Record the fact that an XSL stylesheet includes other sub-stylesheets
76 * </li><li>
77 * Record the fact that java files must be recompiled if the ant build file changes
78 * </li></ul>
79 *
80 * @ant.task category="filesystem"
81 * @since Ant 1.4
82 */
83 public class DependSet extends MatchingTask {
84
85 private static final ResourceSelector NOT_EXISTS = new Not(new Exists());
86 private static final ResourceComparator DATE
87 = new org.apache.tools.ant.types.resources.comparators.Date();
88 private static final ResourceComparator REVERSE_DATE = new Reverse(DATE);
89
90 private static final class NonExistent extends Restrict {
91 private NonExistent(ResourceCollection rc) {
92 super.add(rc);
93 super.add(NOT_EXISTS);
94 }
95 }
96
97 private static final class HideMissingBasedir
98 implements ResourceCollection {
99 private FileSet fs;
100
101 private HideMissingBasedir(FileSet fs) {
102 this.fs = fs;
103 }
104 public Iterator iterator() {
105 return basedirExists() ? fs.iterator() : Resources.EMPTY_ITERATOR;
106 }
107 public int size() {
108 return basedirExists() ? fs.size() : 0;
109 }
110 public boolean isFilesystemOnly() {
111 return true;
112 }
113 private boolean basedirExists() {
114 File basedir = fs.getDir();
115 //trick to evoke "basedir not set" if null:
116 return basedir == null || basedir.exists();
117 }
118 }
119
120 private Union sources = null;
121 private Path targets = null;
122
123 /**
124 * Create a nested sources element.
125 * @return a Union instance.
126 */
127 public synchronized Union createSources() {
128 sources = (sources == null) ? new Union() : sources;
129 return sources;
130 }
131
132 /**
133 * Add a set of source files.
134 * @param fs the FileSet to add.
135 */
136 public void addSrcfileset(FileSet fs) {
137 createSources().add(fs);
138 }
139
140 /**
141 * Add a list of source files.
142 * @param fl the FileList to add.
143 */
144 public void addSrcfilelist(FileList fl) {
145 createSources().add(fl);
146 }
147
148 /**
149 * Create a nested targets element.
150 * @return a Union instance.
151 */
152 public synchronized Path createTargets() {
153 targets = (targets == null) ? new Path(getProject()) : targets;
154 return targets;
155 }
156
157 /**
158 * Add a set of target files.
159 * @param fs the FileSet to add.
160 */
161 public void addTargetfileset(FileSet fs) {
162 createTargets().add(new HideMissingBasedir(fs));
163 }
164
165 /**
166 * Add a list of target files.
167 * @param fl the FileList to add.
168 */
169 public void addTargetfilelist(FileList fl) {
170 createTargets().add(fl);
171 }
172
173 /**
174 * Execute the task.
175 * @throws BuildException if errors occur.
176 */
177 public void execute() throws BuildException {
178 if (sources == null) {
179 throw new BuildException(
180 "At least one set of source resources must be specified");
181 }
182 if (targets == null) {
183 throw new BuildException(
184 "At least one set of target files must be specified");
185 }
186 //no sources = nothing to compare; no targets = nothing to delete:
187 if (sources.size() > 0 && targets.size() > 0 && !uptodate(sources, targets)) {
188 log("Deleting all target files.", Project.MSG_VERBOSE);
189 Delete delete = new Delete();
190 delete.bindToOwner(this);
191 delete.add(targets);
192 delete.perform();
193 }
194 }
195
196 private boolean uptodate(ResourceCollection src, ResourceCollection target) {
197 org.apache.tools.ant.types.resources.selectors.Date datesel
198 = new org.apache.tools.ant.types.resources.selectors.Date();
199 datesel.setMillis(System.currentTimeMillis());
200 datesel.setWhen(TimeComparison.AFTER);
201 logFuture(targets, datesel);
202
203 int neTargets = new NonExistent(targets).size();
204 if (neTargets > 0) {
205 log(neTargets + " nonexistent targets", Project.MSG_VERBOSE);
206 return false;
207 }
208 FileResource oldestTarget = (FileResource) getOldest(targets);
209 log(oldestTarget + " is oldest target file", Project.MSG_VERBOSE);
210
211 logFuture(sources, datesel);
212
213 int neSources = new NonExistent(sources).size();
214 if (neSources > 0) {
215 log(neSources + " nonexistent sources", Project.MSG_VERBOSE);
216 return false;
217 }
218 Resource newestSource = (Resource) getNewest(sources);
219 log(newestSource.toLongString() + " is newest source", Project.MSG_VERBOSE);
220 return oldestTarget.getLastModified() >= newestSource.getLastModified();
221 }
222
223 private void logFuture(ResourceCollection rc, ResourceSelector rsel) {
224 Restrict r = new Restrict();
225 r.add(rsel);
226 r.add(rc);
227 for (Iterator i = r.iterator(); i.hasNext();) {
228 log("Warning: " + i.next() + " modified in the future.", Project.MSG_WARN);
229 }
230 }
231
232 private Resource getXest(ResourceCollection rc, ResourceComparator c) {
233 Iterator i = rc.iterator();
234 if (!i.hasNext()) {
235 return null;
236
237 }
238 Resource xest = (Resource) i.next();
239 while (i.hasNext()) {
240 Resource next = (Resource) i.next();
241 if (c.compare(xest, next) < 0) {
242 xest = next;
243 }
244 }
245 return xest;
246 }
247
248 private Resource getOldest(ResourceCollection rc) {
249 return getXest(rc, REVERSE_DATE);
250 }
251
252 private Resource getNewest(ResourceCollection rc) {
253 return getXest(rc, DATE);
254 }
255
256 }