001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant.taskdefs;
020:
021: import java.io.File;
022: import java.util.Iterator;
023: import org.apache.tools.ant.BuildException;
024: import org.apache.tools.ant.Project;
025: import org.apache.tools.ant.types.Path;
026: import org.apache.tools.ant.types.FileSet;
027: import org.apache.tools.ant.types.FileList;
028: import org.apache.tools.ant.types.Resource;
029: import org.apache.tools.ant.types.TimeComparison;
030: import org.apache.tools.ant.types.ResourceCollection;
031: import org.apache.tools.ant.types.resources.Sort;
032: import org.apache.tools.ant.types.resources.Union;
033: import org.apache.tools.ant.types.resources.Restrict;
034: import org.apache.tools.ant.types.resources.Resources;
035: import org.apache.tools.ant.types.resources.FileResource;
036: import org.apache.tools.ant.types.resources.selectors.Not;
037: import org.apache.tools.ant.types.resources.selectors.Exists;
038: import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
039: import org.apache.tools.ant.types.resources.comparators.Reverse;
040: import org.apache.tools.ant.types.resources.comparators.ResourceComparator;
041:
042: /**
043: * Examines and removes out of date target files. If any of the target files
044: * are out of date with respect to any of the source files, all target
045: * files are removed. This is useful where dependencies cannot be
046: * computed (for example, dynamically interpreted parameters or files
047: * that need to stay in synch but are not directly linked) or where
048: * the ant task in question could compute them but does not (for
049: * example, the linked DTD for an XML file using the XSLT task).
050: *
051: * nested arguments:
052: * <ul>
053: * <li>sources (resource union describing the source resources to examine)
054: * <li>srcfileset (fileset describing the source files to examine)
055: * <li>srcfilelist (filelist describing the source files to examine)
056: * <li>targets (path describing the target files to examine)
057: * <li>targetfileset (fileset describing the target files to examine)
058: * <li>targetfilelist (filelist describing the target files to examine)
059: * </ul>
060: * At least one of both source and target entities is required.
061: * <p>
062: * This task will examine each of the sources against each of the target files. If
063: * any target files are out of date with respect to any of the sources, all targets
064: * are removed. If any sources or targets do not exist, all targets are removed.
065: * Hint: If missing files should be ignored, specify them as include patterns
066: * in filesets, rather than using filelists.
067: * </p><p>
068: * This task attempts to optimize speed of dependency checking
069: * by comparing only the dates of the oldest target file and the newest source.
070: * </p><p>
071: * Example uses:
072: * <ul><li>
073: * Record the fact that an XML file must be up to date with respect to its XSD
074: * (Schema file), even though the XML file itself includes no reference to its XSD.
075: * </li><li>
076: * Record the fact that an XSL stylesheet includes other sub-stylesheets
077: * </li><li>
078: * Record the fact that java files must be recompiled if the ant build file changes
079: * </li></ul>
080: *
081: * @ant.task category="filesystem"
082: * @since Ant 1.4
083: */
084: public class DependSet extends MatchingTask {
085:
086: private static final ResourceSelector NOT_EXISTS = new Not(
087: new Exists());
088: private static final ResourceComparator DATE_ASC = new org.apache.tools.ant.types.resources.comparators.Date();
089: private static final ResourceComparator DATE_DESC = new Reverse(
090: DATE_ASC);
091:
092: private static class NonExistent extends Restrict {
093: private NonExistent(ResourceCollection rc) {
094: super .add(rc);
095: super .add(NOT_EXISTS);
096: }
097: }
098:
099: private static class Xest extends Sort {
100: private Xest(ResourceCollection rc, ResourceComparator c) {
101: super .add(c);
102: super .add(rc);
103: }
104: }
105:
106: private static class Oldest extends Xest {
107: private Oldest(ResourceCollection rc) {
108: super (rc, DATE_ASC);
109: }
110: }
111:
112: private static class Newest extends Xest {
113: private Newest(ResourceCollection rc) {
114: super (rc, DATE_DESC);
115: }
116: }
117:
118: private static class HideMissingBasedir implements
119: ResourceCollection {
120: private FileSet fs;
121:
122: private HideMissingBasedir(FileSet fs) {
123: this .fs = fs;
124: }
125:
126: public Iterator iterator() {
127: return basedirExists() ? fs.iterator()
128: : Resources.EMPTY_ITERATOR;
129: }
130:
131: public int size() {
132: return basedirExists() ? fs.size() : 0;
133: }
134:
135: public boolean isFilesystemOnly() {
136: return true;
137: }
138:
139: private boolean basedirExists() {
140: File basedir = fs.getDir();
141: //trick to evoke "basedir not set" if null:
142: return basedir == null || basedir.exists();
143: }
144: }
145:
146: private Union sources = null;
147: private Path targets = null;
148:
149: /**
150: * Create a nested sources element.
151: * @return a Union instance.
152: */
153: public synchronized Union createSources() {
154: sources = (sources == null) ? new Union() : sources;
155: return sources;
156: }
157:
158: /**
159: * Add a set of source files.
160: * @param fs the FileSet to add.
161: */
162: public void addSrcfileset(FileSet fs) {
163: createSources().add(fs);
164: }
165:
166: /**
167: * Add a list of source files.
168: * @param fl the FileList to add.
169: */
170: public void addSrcfilelist(FileList fl) {
171: createSources().add(fl);
172: }
173:
174: /**
175: * Create a nested targets element.
176: * @return a Union instance.
177: */
178: public synchronized Path createTargets() {
179: targets = (targets == null) ? new Path(getProject()) : targets;
180: return targets;
181: }
182:
183: /**
184: * Add a set of target files.
185: * @param fs the FileSet to add.
186: */
187: public void addTargetfileset(FileSet fs) {
188: createTargets().add(new HideMissingBasedir(fs));
189: }
190:
191: /**
192: * Add a list of target files.
193: * @param fl the FileList to add.
194: */
195: public void addTargetfilelist(FileList fl) {
196: createTargets().add(fl);
197: }
198:
199: /**
200: * Execute the task.
201: * @throws BuildException if errors occur.
202: */
203: public void execute() throws BuildException {
204: if (sources == null) {
205: throw new BuildException(
206: "At least one set of source resources must be specified");
207: }
208: if (targets == null) {
209: throw new BuildException(
210: "At least one set of target files must be specified");
211: }
212: //no sources = nothing to compare; no targets = nothing to delete:
213: if (sources.size() > 0 && targets.size() > 0
214: && !uptodate(sources, targets)) {
215: log("Deleting all target files.", Project.MSG_VERBOSE);
216: Delete delete = new Delete();
217: delete.bindToOwner(this );
218: delete.add(targets);
219: delete.perform();
220: }
221: }
222:
223: private boolean uptodate(ResourceCollection src,
224: ResourceCollection target) {
225: org.apache.tools.ant.types.resources.selectors.Date datesel = new org.apache.tools.ant.types.resources.selectors.Date();
226: datesel.setMillis(System.currentTimeMillis());
227: datesel.setWhen(TimeComparison.AFTER);
228: logFuture(targets, datesel);
229:
230: int neTargets = new NonExistent(targets).size();
231: if (neTargets > 0) {
232: log(neTargets + " nonexistent targets", Project.MSG_VERBOSE);
233: return false;
234: }
235: FileResource oldestTarget = (FileResource) (new Oldest(targets)
236: .iterator().next());
237: log(oldestTarget + " is oldest target file",
238: Project.MSG_VERBOSE);
239:
240: logFuture(sources, datesel);
241:
242: int neSources = new NonExistent(sources).size();
243: if (neSources > 0) {
244: log(neSources + " nonexistent sources", Project.MSG_VERBOSE);
245: return false;
246: }
247: Resource newestSource = (Resource) (new Newest(sources)
248: .iterator().next());
249: log(newestSource.toLongString() + " is newest source",
250: Project.MSG_VERBOSE);
251: return oldestTarget.getLastModified() >= newestSource
252: .getLastModified();
253: }
254:
255: private void logFuture(ResourceCollection rc, ResourceSelector rsel) {
256: Restrict r = new Restrict();
257: r.add(rsel);
258: r.add(rc);
259: for (Iterator i = r.iterator(); i.hasNext();) {
260: log("Warning: " + i.next() + " modified in the future.",
261: Project.MSG_WARN);
262: }
263: }
264: }
|