View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.clean;
20  
21  import java.io.File;
22  import java.io.IOException;
23  
24  import org.apache.maven.execution.MavenSession;
25  import org.apache.maven.plugin.AbstractMojo;
26  import org.apache.maven.plugin.MojoExecutionException;
27  import org.apache.maven.plugins.annotations.Mojo;
28  import org.apache.maven.plugins.annotations.Parameter;
29  
30  /**
31   * Goal which cleans the build.
32   * <p>
33   * This attempts to clean a project's working directory of the files that were generated at build-time. By default, it
34   * discovers and deletes the directories configured in <code>project.build.directory</code>,
35   * <code>project.build.outputDirectory</code>, <code>project.build.testOutputDirectory</code>, and
36   * <code>project.reporting.outputDirectory</code>.
37   * </p>
38   * <p>
39   * Files outside the default may also be included in the deletion by configuring the <code>filesets</code> tag.
40   * </p>
41   *
42   * @author <a href="mailto:evenisse@maven.org">Emmanuel Venisse</a>
43   * @see org.apache.maven.plugins.clean.Fileset
44   * @since 2.0
45   */
46  @Mojo(name = "clean", threadSafe = true)
47  public class CleanMojo extends AbstractMojo {
48  
49      public static final String FAST_MODE_BACKGROUND = "background";
50  
51      public static final String FAST_MODE_AT_END = "at-end";
52  
53      public static final String FAST_MODE_DEFER = "defer";
54  
55      /**
56       * This is where build results go.
57       */
58      @Parameter(defaultValue = "${project.build.directory}", readonly = true, required = true)
59      private File directory;
60  
61      /**
62       * This is where compiled classes go.
63       */
64      @Parameter(defaultValue = "${project.build.outputDirectory}", readonly = true, required = true)
65      private File outputDirectory;
66  
67      /**
68       * This is where compiled test classes go.
69       */
70      @Parameter(defaultValue = "${project.build.testOutputDirectory}", readonly = true, required = true)
71      private File testOutputDirectory;
72  
73      /**
74       * This is where the site plugin generates its pages.
75       *
76       * @since 2.1.1
77       */
78      @Parameter(defaultValue = "${project.build.outputDirectory}", readonly = true, required = true)
79      private File reportDirectory;
80  
81      /**
82       * Sets whether the plugin runs in verbose mode. As of plugin version 2.3, the default value is derived from Maven's
83       * global debug flag (compare command line switch <code>-X</code>). <br/>
84       * Starting with <b>3.0.0</b> the property has been renamed from <code>clean.verbose</code> to
85       * <code>maven.clean.verbose</code>.
86       *
87       * @since 2.1
88       */
89      @Parameter(property = "maven.clean.verbose")
90      private Boolean verbose;
91  
92      /**
93       * The list of file sets to delete, in addition to the default directories. For example:
94       *
95       * <pre>
96       * &lt;filesets&gt;
97       *   &lt;fileset&gt;
98       *     &lt;directory&gt;src/main/generated&lt;/directory&gt;
99       *     &lt;followSymlinks&gt;false&lt;/followSymlinks&gt;
100      *     &lt;useDefaultExcludes&gt;true&lt;/useDefaultExcludes&gt;
101      *     &lt;includes&gt;
102      *       &lt;include&gt;*.java&lt;/include&gt;
103      *     &lt;/includes&gt;
104      *     &lt;excludes&gt;
105      *       &lt;exclude&gt;Template*&lt;/exclude&gt;
106      *     &lt;/excludes&gt;
107      *   &lt;/fileset&gt;
108      * &lt;/filesets&gt;
109      * </pre>
110      *
111      * @since 2.1
112      */
113     @Parameter
114     private Fileset[] filesets;
115 
116     /**
117      * Sets whether the plugin should follow symbolic links while deleting files from the default output directories of
118      * the project. Not following symlinks requires more IO operations and heap memory, regardless whether symlinks are
119      * actually present. So projects with a huge output directory that knowingly does not contain symlinks can improve
120      * performance by setting this parameter to <code>true</code>. <br/>
121      * Starting with <code>3.0.0</code> the property has been renamed from <code>clean.followSymLinks</code> to
122      * <code>maven.clean.followSymLinks</code>.
123      *
124      * @since 2.1
125      */
126     @Parameter(property = "maven.clean.followSymLinks", defaultValue = "false")
127     private boolean followSymLinks;
128 
129     /**
130      * Disables the plugin execution. <br/>
131      * Starting with <code>3.0.0</code> the property has been renamed from <code>clean.skip</code> to
132      * <code>maven.clean.skip</code>.
133      *
134      * @since 2.2
135      */
136     @Parameter(property = "maven.clean.skip", defaultValue = "false")
137     private boolean skip;
138 
139     /**
140      * Indicates whether the build will continue even if there are clean errors.
141      *
142      * @since 2.2
143      */
144     @Parameter(property = "maven.clean.failOnError", defaultValue = "true")
145     private boolean failOnError;
146 
147     /**
148      * Indicates whether the plugin should undertake additional attempts (after a short delay) to delete a file if the
149      * first attempt failed. This is meant to help deleting files that are temporarily locked by third-party tools like
150      * virus scanners or search indexing.
151      *
152      * @since 2.4.2
153      */
154     @Parameter(property = "maven.clean.retryOnError", defaultValue = "true")
155     private boolean retryOnError;
156 
157     /**
158      * Disables the deletion of the default output directories configured for a project. If set to <code>true</code>,
159      * only the files/directories selected via the parameter {@link #filesets} will be deleted. <br/>
160      * Starting with <b>3.0.0</b> the property has been renamed from <code>clean.excludeDefaultDirectories</code> to
161      * <code>maven.clean.excludeDefaultDirectories</code>.
162      *
163      * @since 2.3
164      */
165     @Parameter(property = "maven.clean.excludeDefaultDirectories", defaultValue = "false")
166     private boolean excludeDefaultDirectories;
167 
168     /**
169      * Enables fast clean if possible. If set to <code>true</code>, when the plugin is executed, a directory to
170      * be deleted will be atomically moved inside the <code>maven.clean.fastDir</code> directory and a thread will
171      * be launched to delete the needed files in the background.  When the build is completed, maven will wait
172      * until all the files have been deleted.  If any problem occurs during the atomic move of the directories,
173      * the plugin will default to the traditional deletion mechanism.
174      *
175      * @since 3.2
176      */
177     @Parameter(property = "maven.clean.fast", defaultValue = "false")
178     private boolean fast;
179 
180     /**
181      * When fast clean is specified, the <code>fastDir</code> property will be used as the location where directories
182      * to be deleted will be moved prior to background deletion.  If not specified, the
183      * <code>${maven.multiModuleProjectDirectory}/target/.clean</code> directory will be used.  If the
184      * <code>${build.directory}</code> has been modified, you'll have to adjust this property explicitly.
185      * In order for fast clean to work correctly, this directory and the various directories that will be deleted
186      * should usually reside on the same volume. The exact conditions are system dependant though, but if an atomic
187      * move is not supported, the standard deletion mechanism will be used.
188      *
189      * @since 3.2
190      * @see #fast
191      */
192     @Parameter(property = "maven.clean.fastDir")
193     private File fastDir;
194 
195     /**
196      * Mode to use when using fast clean.  Values are: <code>background</code> to start deletion immediately and
197      * waiting for all files to be deleted when the session ends, <code>at-end</code> to indicate that the actual
198      * deletion should be performed synchronously when the session ends, or <code>defer</code> to specify that
199      * the actual file deletion should be started in the background when the session ends (this should only be used
200      * when maven is embedded in a long running process).
201      *
202      * @since 3.2
203      * @see #fast
204      */
205     @Parameter(property = "maven.clean.fastMode", defaultValue = FAST_MODE_BACKGROUND)
206     private String fastMode;
207 
208     @Parameter(defaultValue = "${session}", readonly = true)
209     private MavenSession session;
210 
211     /**
212      * Deletes file-sets in the following project build directory order: (source) directory, output directory, test
213      * directory, report directory, and then the additional file-sets.
214      *
215      * @throws MojoExecutionException When a directory failed to get deleted.
216      * @see org.apache.maven.plugin.Mojo#execute()
217      */
218     public void execute() throws MojoExecutionException {
219         if (skip) {
220             getLog().info("Clean is skipped.");
221             return;
222         }
223 
224         String multiModuleProjectDirectory =
225                 session != null ? session.getSystemProperties().getProperty("maven.multiModuleProjectDirectory") : null;
226         File fastDir;
227         if (fast && this.fastDir != null) {
228             fastDir = this.fastDir;
229         } else if (fast && multiModuleProjectDirectory != null) {
230             fastDir = new File(multiModuleProjectDirectory, "target/.clean");
231         } else {
232             fastDir = null;
233             if (fast) {
234                 getLog().warn("Fast clean requires maven 3.3.1 or newer, "
235                         + "or an explicit directory to be specified with the 'fastDir' configuration of "
236                         + "this plugin, or the 'maven.clean.fastDir' user property to be set.");
237             }
238         }
239         if (fast
240                 && !FAST_MODE_BACKGROUND.equals(fastMode)
241                 && !FAST_MODE_AT_END.equals(fastMode)
242                 && !FAST_MODE_DEFER.equals(fastMode)) {
243             throw new IllegalArgumentException("Illegal value '" + fastMode + "' for fastMode. Allowed values are '"
244                     + FAST_MODE_BACKGROUND + "', '" + FAST_MODE_AT_END + "' and '" + FAST_MODE_DEFER + "'.");
245         }
246 
247         Cleaner cleaner = new Cleaner(session, getLog(), isVerbose(), fastDir, fastMode);
248 
249         try {
250             for (File directoryItem : getDirectories()) {
251                 if (directoryItem != null) {
252                     cleaner.delete(directoryItem, null, followSymLinks, failOnError, retryOnError);
253                 }
254             }
255 
256             if (filesets != null) {
257                 for (Fileset fileset : filesets) {
258                     if (fileset.getDirectory() == null) {
259                         throw new MojoExecutionException("Missing base directory for " + fileset);
260                     }
261                     final String[] includes = fileset.getIncludes();
262                     final String[] excludes = fileset.getExcludes();
263                     final boolean useDefaultExcludes = fileset.isUseDefaultExcludes();
264                     final GlobSelector selector;
265                     if ((includes != null && includes.length != 0)
266                             || (excludes != null && excludes.length != 0)
267                             || useDefaultExcludes) {
268                         selector = new GlobSelector(includes, excludes, useDefaultExcludes);
269                     } else {
270                         selector = null;
271                     }
272                     cleaner.delete(
273                             fileset.getDirectory(), selector, fileset.isFollowSymlinks(), failOnError, retryOnError);
274                 }
275             }
276         } catch (IOException e) {
277             throw new MojoExecutionException("Failed to clean project: " + e.getMessage(), e);
278         }
279     }
280 
281     /**
282      * Indicates whether verbose output is enabled.
283      *
284      * @return <code>true</code> if verbose output is enabled, <code>false</code> otherwise.
285      */
286     private boolean isVerbose() {
287         return (verbose != null) ? verbose : getLog().isDebugEnabled();
288     }
289 
290     /**
291      * Gets the directories to clean (if any). The returned array may contain null entries.
292      *
293      * @return The directories to clean or an empty array if none, never <code>null</code>.
294      */
295     private File[] getDirectories() {
296         File[] directories;
297         if (excludeDefaultDirectories) {
298             directories = new File[0];
299         } else {
300             directories = new File[] {directory, outputDirectory, testOutputDirectory, reportDirectory};
301         }
302         return directories;
303     }
304 }