aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/DependSet.java
blob: dc42beb1a65177a3e6cc567101b8769ef267e963 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package org.apache.tools.ant.taskdefs;

import java.io.File;
import java.util.Date;
import java.util.Iterator;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.FileList;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.ResourceCollection;
import org.apache.tools.ant.types.TimeComparison;
import org.apache.tools.ant.types.resources.Resources;
import org.apache.tools.ant.types.resources.Restrict;
import org.apache.tools.ant.types.resources.Union;
import org.apache.tools.ant.types.resources.comparators.ResourceComparator;
import org.apache.tools.ant.types.resources.comparators.Reverse;
import org.apache.tools.ant.types.resources.selectors.Exists;
import org.apache.tools.ant.types.resources.selectors.Not;
import org.apache.tools.ant.types.resources.selectors.ResourceSelector;

/**
 * Examines and removes out of date target files.  If any of the target files
 * are out of date with respect to any of the source files, all target
 * files are removed.  This is useful where dependencies cannot be
 * computed (for example, dynamically interpreted parameters or files
 * that need to stay in synch but are not directly linked) or where
 * the ant task in question could compute them but does not (for
 * example, the linked DTD for an XML file using the XSLT task).
 *
 * nested arguments:
 * <ul>
 * <li>sources        (resource union describing the source resources to examine)
 * <li>srcfileset     (fileset describing the source files to examine)
 * <li>srcfilelist    (filelist describing the source files to examine)
 * <li>targets        (path describing the target files to examine)
 * <li>targetfileset  (fileset describing the target files to examine)
 * <li>targetfilelist (filelist describing the target files to examine)
 * </ul>
 * At least one of both source and target entities is required.
 * <p>
 * This task will examine each of the sources against each of the target files. If
 * any target files are out of date with respect to any of the sources, all targets
 * are removed. If any sources or targets do not exist, all targets are removed.
 * Hint: If missing files should be ignored, specify them as include patterns
 * in filesets, rather than using filelists.
 * </p><p>
 * This task attempts to optimize speed of dependency checking
 * by comparing only the dates of the oldest target file and the newest source.
 * </p><p>
 * Example uses:
 * <ul><li>
 * Record the fact that an XML file must be up to date with respect to its XSD
 * (Schema file), even though the XML file itself includes no reference to its XSD.
 * </li><li>
 * Record the fact that an XSL stylesheet includes other sub-stylesheets
 * </li><li>
 * Record the fact that java files must be recompiled if the ant build file changes
 * </li></ul>
 *
 * @ant.task category="filesystem"
 * @since Ant 1.4
 */
public class DependSet extends MatchingTask {

    private static final ResourceSelector NOT_EXISTS = new Not(new Exists());
    private static final ResourceComparator DATE
        = new org.apache.tools.ant.types.resources.comparators.Date();
    private static final ResourceComparator REVERSE_DATE = new Reverse(DATE);

    private static final class NonExistent extends Restrict {
        private NonExistent(ResourceCollection rc) {
            super.add(rc);
            super.add(NOT_EXISTS);
        }
    }

    private static final class HideMissingBasedir
        implements ResourceCollection {
        private FileSet fs;

        private HideMissingBasedir(FileSet fs) {
            this.fs = fs;
        }
        public Iterator<Resource> iterator() {
            return basedirExists() ? fs.iterator() : Resources.EMPTY_ITERATOR;
        }
        public int size() {
            return basedirExists() ? fs.size() : 0;
        }
        public boolean isFilesystemOnly() {
            return true;
        }
        private boolean basedirExists() {
            File basedir = fs.getDir();
            //trick to evoke "basedir not set" if null:
            return basedir == null || basedir.exists();
        }
    }

    private Union sources = null;
    private Path targets = null;

    private boolean verbose;

    /**
     * Create a nested sources element.
     * @return a Union instance.
     */
    public synchronized Union createSources() {
        sources = (sources == null) ? new Union() : sources;
        return sources;
    }

    /**
     * Add a set of source files.
     * @param fs the FileSet to add.
     */
    public void addSrcfileset(FileSet fs) {
        createSources().add(fs);
    }

    /**
     * Add a list of source files.
     * @param fl the FileList to add.
     */
    public void addSrcfilelist(FileList fl) {
        createSources().add(fl);
    }

    /**
     * Create a nested targets element.
     * @return a Union instance.
     */
    public synchronized Path createTargets() {
        targets = (targets == null) ? new Path(getProject()) : targets;
        return targets;
    }

    /**
     * Add a set of target files.
     * @param fs the FileSet to add.
     */
    public void addTargetfileset(FileSet fs) {
        createTargets().add(new HideMissingBasedir(fs));
    }

    /**
     * Add a list of target files.
     * @param fl the FileList to add.
     */
    public void addTargetfilelist(FileList fl) {
        createTargets().add(fl);
    }

    /**
     * In verbose mode missing targets and sources as well as the
     * modification times of the newest source and latest target will
     * be logged as info.
     *
     * <p>All deleted files will be logged as well.</p>
     *
     * @since Ant 1.8.0
     */
    public void setVerbose(boolean b) {
        verbose = b;
    }

    /**
     * Execute the task.
     * @throws BuildException if errors occur.
     */
    public void execute() throws BuildException {
        if (sources == null) {
          throw new BuildException(
              "At least one set of source resources must be specified");
        }
        if (targets == null) {
          throw new BuildException(
              "At least one set of target files must be specified");
        }
        //no sources = nothing to compare; no targets = nothing to delete:
        if (sources.size() > 0 && targets.size() > 0 && !uptodate(sources, targets)) {
           log("Deleting all target files.", Project.MSG_VERBOSE);
           if (verbose) {
               String[] t = targets.list();
               for (int i = 0; i < t.length; i++) {
                   log("Deleting " + t[i]);
               }
           }
           Delete delete = new Delete();
           delete.bindToOwner(this);
           delete.add(targets);
           delete.perform();
        }
    }

    private boolean uptodate(ResourceCollection src, ResourceCollection target) {
        org.apache.tools.ant.types.resources.selectors.Date datesel
            = new org.apache.tools.ant.types.resources.selectors.Date();
        datesel.setMillis(System.currentTimeMillis());
        datesel.setWhen(TimeComparison.AFTER);
        // don't whine because a file has changed during the last
        // second (or whathever our current granularity may be)
        datesel.setGranularity(0);
        logFuture(targets, datesel);

        NonExistent missingTargets = new NonExistent(targets);
        int neTargets = missingTargets.size();
        if (neTargets > 0) {
            log(neTargets + " nonexistent targets", Project.MSG_VERBOSE);
            logMissing(missingTargets, "target");
            return false;
        }
        Resource oldestTarget = getOldest(targets);
        logWithModificationTime(oldestTarget, "oldest target file");

        logFuture(sources, datesel);

        NonExistent missingSources = new NonExistent(sources);
        int neSources = missingSources.size();
        if (neSources > 0) {
            log(neSources + " nonexistent sources", Project.MSG_VERBOSE);
            logMissing(missingSources, "source");
            return false;
        }
        Resource newestSource = (Resource) getNewest(sources);
        logWithModificationTime(newestSource, "newest source");
        return oldestTarget.getLastModified() >= newestSource.getLastModified();
    }

    private void logFuture(ResourceCollection rc, ResourceSelector rsel) {
        Restrict r = new Restrict();
        r.add(rsel);
        r.add(rc);
        for (Resource res : r) {
            log("Warning: " + res + " modified in the future.", Project.MSG_WARN);
        }
    }

    private Resource getXest(ResourceCollection rc, ResourceComparator c) {
        Iterator<Resource> i = rc.iterator();
        if (!i.hasNext()) {
            return null;

        }
        Resource xest = i.next();
        while (i.hasNext()) {
            Resource next = i.next();
            if (c.compare(xest, next) < 0) {
                xest = next;
            }
        }
        return xest;
    }

    private Resource getOldest(ResourceCollection rc) {
        return getXest(rc, REVERSE_DATE);
    }

    private Resource getNewest(ResourceCollection rc) {
        return getXest(rc, DATE);
    }

    private void logWithModificationTime(Resource r, String what) {
        log(r.toLongString() + " is " + what + ", modified at "
            + new Date(r.getLastModified()),
            verbose ? Project.MSG_INFO : Project.MSG_VERBOSE);
    }

    private void logMissing(ResourceCollection missing, String what) {
        if (verbose) {
            for (Resource r : missing) {
                log("Expected " + what + " " + r.toLongString()
                    + " is missing.");
            }
        }
    }
}