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 * Disables the plugin execution. <br/> 133 * Starting with <code>3.0.0</code> the property has been renamed from <code>clean.skip</code> to 134 * <code>maven.clean.skip</code>. 135 * 136 * @since 2.2 137 */ 138 @Parameter(property = "maven.clean.skip", defaultValue = "false") 139 private boolean skip; 140 141 /** 142 * Indicates whether the build will continue even if there are clean errors. 143 * 144 * @since 2.2 145 */ 146 @Parameter(property = "maven.clean.failOnError", defaultValue = "true") 147 private boolean failOnError; 148 149 /** 150 * Indicates whether the plugin should undertake additional attempts (after a short delay) to delete a file if the 151 * first attempt failed. This is meant to help deleting files that are temporarily locked by third-party tools like 152 * virus scanners or search indexing. 153 * 154 * @since 2.4.2 155 */ 156 @Parameter(property = "maven.clean.retryOnError", defaultValue = "true") 157 private boolean retryOnError; 158 159 /** 160 * Disables the deletion of the default output directories configured for a project. If set to <code>true</code>, 161 * only the files/directories selected via the parameter {@link #filesets} will be deleted. <br/> 162 * Starting with <b>3.0.0</b> the property has been renamed from <code>clean.excludeDefaultDirectories</code> to 163 * <code>maven.clean.excludeDefaultDirectories</code>. 164 * 165 * @since 2.3 166 */ 167 @Parameter(property = "maven.clean.excludeDefaultDirectories", defaultValue = "false") 168 private boolean excludeDefaultDirectories; 169 170 /** 171 * Enables fast clean if possible. If set to <code>true</code>, when the plugin is executed, a directory to 172 * be deleted will be atomically moved inside the <code>maven.clean.fastDir</code> directory and a thread will 173 * be launched to delete the needed files in the background. When the build is completed, maven will wait 174 * until all the files have been deleted. If any problem occurs during the atomic move of the directories, 175 * the plugin will default to the traditional deletion mechanism. 176 * 177 * @since 3.2 178 */ 179 @Parameter(property = "maven.clean.fast", defaultValue = "false") 180 private boolean fast; 181 182 /** 183 * When fast clean is specified, the <code>fastDir</code> property will be used as the location where directories 184 * to be deleted will be moved prior to background deletion. If not specified, the 185 * <code>${maven.multiModuleProjectDirectory}/target/.clean</code> directory will be used. If the 186 * <code>${build.directory}</code> has been modified, you'll have to adjust this property explicitly. 187 * In order for fast clean to work correctly, this directory and the various directories that will be deleted 188 * should usually reside on the same volume. The exact conditions are system-dependent though, but if an atomic 189 * move is not supported, the standard deletion mechanism will be used. 190 * 191 * @see #fast 192 * @since 3.2 193 */ 194 @Parameter(property = "maven.clean.fastDir") 195 private File fastDir; 196 197 /** 198 * Mode to use when using fast clean. Values are: <code>background</code> to start deletion immediately and 199 * waiting for all files to be deleted when the session ends, <code>at-end</code> to indicate that the actual 200 * deletion should be performed synchronously when the session ends, or <code>defer</code> to specify that 201 * the actual file deletion should be started in the background when the session ends. This should only be used 202 * when maven is embedded in a long-running process. 203 * 204 * @see #fast 205 * @since 3.2 206 */ 207 @Parameter(property = "maven.clean.fastMode", defaultValue = FAST_MODE_BACKGROUND) 208 private String fastMode; 209 210 @Parameter(defaultValue = "${session}", readonly = true) 211 private MavenSession session; 212 213 /** 214 * Deletes file-sets in the following project build directory order: (source) directory, output directory, test 215 * directory, report directory, and then the additional file-sets. 216 * 217 * @throws MojoExecutionException When a directory failed to get deleted. 218 * @see org.apache.maven.plugin.Mojo#execute() 219 */ 220 public void execute() throws MojoExecutionException { 221 if (skip) { 222 getLog().info("Clean is skipped."); 223 return; 224 } 225 226 String multiModuleProjectDirectory = 227 session != null ? session.getSystemProperties().getProperty("maven.multiModuleProjectDirectory") : null; 228 Path fastDir; 229 if (fast && this.fastDir != null) { 230 fastDir = toNullablePath(this.fastDir); 231 } else if (fast && multiModuleProjectDirectory != null) { 232 fastDir = FileSystems.getDefault() 233 .getPath(multiModuleProjectDirectory) 234 .resolve("target") 235 .resolve(".clean"); 236 } else { 237 fastDir = null; 238 if (fast) { 239 getLog().warn("Fast clean requires maven 3.3.1 or newer, " 240 + "or an explicit directory to be specified with the 'fastDir' configuration of " 241 + "this plugin, or the 'maven.clean.fastDir' user property to be set."); 242 } 243 } 244 if (fast 245 && !FAST_MODE_BACKGROUND.equals(fastMode) 246 && !FAST_MODE_AT_END.equals(fastMode) 247 && !FAST_MODE_DEFER.equals(fastMode)) { 248 throw new IllegalArgumentException("Illegal value '" + fastMode + "' for fastMode. Allowed values are '" 249 + FAST_MODE_BACKGROUND + "', '" + FAST_MODE_AT_END + "' and '" + FAST_MODE_DEFER + "'."); 250 } 251 252 Cleaner cleaner = new Cleaner(session, getLog(), isVerbose(), fastDir, fastMode); 253 254 try { 255 for (Path directoryItem : getDirectories()) { 256 if (directoryItem != null) { 257 cleaner.delete(directoryItem, null, followSymLinks, failOnError, retryOnError); 258 } 259 } 260 261 if (filesets != null) { 262 for (Fileset fileset : filesets) { 263 if (fileset.getDirectory() == null) { 264 throw new MojoExecutionException("Missing base directory for " + fileset); 265 } 266 final String[] includes = fileset.getIncludes(); 267 final String[] excludes = fileset.getExcludes(); 268 final boolean useDefaultExcludes = fileset.isUseDefaultExcludes(); 269 final GlobSelector selector; 270 if ((includes != null && includes.length != 0) 271 || (excludes != null && excludes.length != 0) 272 || useDefaultExcludes) { 273 selector = new GlobSelector(includes, excludes, useDefaultExcludes); 274 } else { 275 selector = null; 276 } 277 cleaner.delete( 278 fileset.getDirectory().toPath(), 279 selector, 280 fileset.isFollowSymlinks(), 281 failOnError, 282 retryOnError); 283 } 284 } 285 } catch (IOException e) { 286 throw new MojoExecutionException("Failed to clean project: " + e.getMessage(), e); 287 } 288 } 289 290 /** 291 * Indicates whether verbose output is enabled. 292 * 293 * @return <code>true</code> if verbose output is enabled, <code>false</code> otherwise. 294 */ 295 private boolean isVerbose() { 296 return (verbose != null) ? verbose : getLog().isDebugEnabled(); 297 } 298 299 /** 300 * Gets the directories to clean (if any). The returned array may contain null entries. 301 * 302 * @return The directories to clean or an empty array if none, never <code>null</code>. 303 */ 304 private Path[] getDirectories() { 305 Path[] directories; 306 if (excludeDefaultDirectories) { 307 directories = new Path[0]; 308 } else { 309 directories = new Path[] { 310 toNullablePath(directory), 311 toNullablePath(outputDirectory), 312 toNullablePath(testOutputDirectory), 313 toNullablePath(reportDirectory) 314 }; 315 } 316 return directories; 317 } 318 319 private static Path toNullablePath(File file) { 320 return file != null ? file.toPath() : null; 321 } 322 }