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