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.IOException; 22 import java.nio.file.Path; 23 import java.nio.file.Paths; 24 25 import org.apache.maven.api.Session; 26 import org.apache.maven.api.di.Inject; 27 import org.apache.maven.api.plugin.Log; 28 import org.apache.maven.api.plugin.MojoException; 29 import org.apache.maven.api.plugin.annotations.Mojo; 30 import org.apache.maven.api.plugin.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") 49 public class CleanMojo implements org.apache.maven.api.plugin.Mojo { 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 @Inject 58 private Log logger; 59 60 /** 61 * This is where build results go. 62 */ 63 @Parameter(defaultValue = "${project.build.directory}", readonly = true, required = true) 64 private Path directory; 65 66 /** 67 * This is where compiled classes go. 68 */ 69 @Parameter(defaultValue = "${project.build.outputDirectory}", readonly = true, required = true) 70 private Path outputDirectory; 71 72 /** 73 * This is where compiled test classes go. 74 */ 75 @Parameter(defaultValue = "${project.build.testOutputDirectory}", readonly = true, required = true) 76 private Path testOutputDirectory; 77 78 /** 79 * This is where the site plugin generates its pages. 80 * 81 * @since 2.1.1 82 */ 83 @Parameter(defaultValue = "${project.build.outputDirectory}", readonly = true, required = true) 84 private Path reportDirectory; 85 86 /** 87 * Sets whether the plugin runs in verbose mode. As of plugin version 2.3, the default value is derived from Maven's 88 * global debug flag (compare command line switch <code>-X</code>). <br/> 89 * Starting with <b>3.0.0</b> the property has been renamed from <code>clean.verbose</code> to 90 * <code>maven.clean.verbose</code>. 91 * 92 * @since 2.1 93 */ 94 @Parameter(property = "maven.clean.verbose") 95 private Boolean verbose; 96 97 /** 98 * The list of file sets to delete, in addition to the default directories. For example: 99 * 100 * <pre> 101 * <filesets> 102 * <fileset> 103 * <directory>src/main/generated</directory> 104 * <followSymlinks>false</followSymlinks> 105 * <useDefaultExcludes>true</useDefaultExcludes> 106 * <includes> 107 * <include>*.java</include> 108 * </includes> 109 * <excludes> 110 * <exclude>Template*</exclude> 111 * </excludes> 112 * </fileset> 113 * </filesets> 114 * </pre> 115 * 116 * @since 2.1 117 */ 118 @Parameter 119 private Fileset[] filesets; 120 121 /** 122 * Sets whether the plugin should follow symbolic links while deleting files from the default output directories of 123 * the project. Not following symlinks requires more IO operations and heap memory, regardless whether symlinks are 124 * actually present. So projects with a huge output directory that knowingly does not contain symlinks can improve 125 * performance by setting this parameter to <code>true</code>. <br/> 126 * Starting with <code>3.0.0</code> the property has been renamed from <code>clean.followSymLinks</code> to 127 * <code>maven.clean.followSymLinks</code>. 128 * 129 * @since 2.1 130 */ 131 @Parameter(property = "maven.clean.followSymLinks", defaultValue = "false") 132 private boolean followSymLinks; 133 134 /** 135 * Disables the plugin execution. <br/> 136 * Starting with <code>3.0.0</code> the property has been renamed from <code>clean.skip</code> to 137 * <code>maven.clean.skip</code>. 138 * 139 * @since 2.2 140 */ 141 @Parameter(property = "maven.clean.skip", defaultValue = "false") 142 private boolean skip; 143 144 /** 145 * Indicates whether the build will continue even if there are clean errors. 146 * 147 * @since 2.2 148 */ 149 @Parameter(property = "maven.clean.failOnError", defaultValue = "true") 150 private boolean failOnError; 151 152 /** 153 * Indicates whether the plugin should undertake additional attempts (after a short delay) to delete a file if the 154 * first attempt failed. This is meant to help deleting files that are temporarily locked by third-party tools like 155 * virus scanners or search indexing. 156 * 157 * @since 2.4.2 158 */ 159 @Parameter(property = "maven.clean.retryOnError", defaultValue = "true") 160 private boolean retryOnError; 161 162 /** 163 * Disables the deletion of the default output directories configured for a project. If set to <code>true</code>, 164 * only the files/directories selected via the parameter {@link #filesets} will be deleted. <br/> 165 * Starting with <b>3.0.0</b> the property has been renamed from <code>clean.excludeDefaultDirectories</code> to 166 * <code>maven.clean.excludeDefaultDirectories</code>. 167 * 168 * @since 2.3 169 */ 170 @Parameter(property = "maven.clean.excludeDefaultDirectories", defaultValue = "false") 171 private boolean excludeDefaultDirectories; 172 173 /** 174 * Enables fast clean if possible. If set to <code>true</code>, when the plugin is executed, a directory to 175 * be deleted will be atomically moved inside the <code>maven.clean.fastDir</code> directory and a thread will 176 * be launched to delete the needed files in the background. When the build is completed, maven will wait 177 * until all the files have been deleted. If any problem occurs during the atomic move of the directories, 178 * the plugin will default to the traditional deletion mechanism. 179 * 180 * @since 3.2 181 */ 182 @Parameter(property = "maven.clean.fast", defaultValue = "false") 183 private boolean fast; 184 185 /** 186 * When fast clean is specified, the <code>fastDir</code> property will be used as the location where directories 187 * to be deleted will be moved prior to background deletion. If not specified, the 188 * <code>${maven.multiModuleProjectDirectory}/target/.clean</code> directory will be used. If the 189 * <code>${build.directory}</code> has been modified, you'll have to adjust this property explicitly. 190 * In order for fast clean to work correctly, this directory and the various directories that will be deleted 191 * should usually reside on the same volume. The exact conditions are system dependant though, but if an atomic 192 * move is not supported, the standard deletion mechanism will be used. 193 * 194 * @since 3.2 195 * @see #fast 196 */ 197 @Parameter(property = "maven.clean.fastDir") 198 private Path fastDir; 199 200 /** 201 * Mode to use when using fast clean. Values are: <code>background</code> to start deletion immediately and 202 * waiting for all files to be deleted when the session ends, <code>at-end</code> to indicate that the actual 203 * deletion should be performed synchronously when the session ends, or <code>defer</code> to specify that 204 * the actual file deletion should be started in the background when the session ends (this should only be used 205 * when maven is embedded in a long running process). 206 * 207 * @since 3.2 208 * @see #fast 209 */ 210 @Parameter(property = "maven.clean.fastMode", defaultValue = FAST_MODE_BACKGROUND) 211 private String fastMode; 212 213 @Inject 214 private Session session; 215 216 /** 217 * Deletes file-sets in the following project build directory order: (source) directory, output directory, test 218 * directory, report directory, and then the additional file-sets. 219 * 220 * @throws MojoException When a directory failed to get deleted. 221 * @see org.apache.maven.api.plugin.Mojo#execute() 222 */ 223 public void execute() { 224 if (skip) { 225 getLog().info("Clean is skipped."); 226 return; 227 } 228 229 String multiModuleProjectDirectory = 230 session != null ? session.getSystemProperties().get("maven.multiModuleProjectDirectory") : null; 231 Path fastDir; 232 if (fast && this.fastDir != null) { 233 fastDir = this.fastDir; 234 } else if (fast && multiModuleProjectDirectory != null) { 235 fastDir = Paths.get(multiModuleProjectDirectory, "target/.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 MojoException("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(), selector, fileset.isFollowSymlinks(), failOnError, retryOnError); 279 } 280 } 281 282 } catch (IOException e) { 283 throw new MojoException("Failed to clean project: " + e.getMessage(), e); 284 } 285 } 286 287 /** 288 * Indicates whether verbose output is enabled. 289 * 290 * @return <code>true</code> if verbose output is enabled, <code>false</code> otherwise. 291 */ 292 private boolean isVerbose() { 293 return (verbose != null) ? verbose : getLog().isDebugEnabled(); 294 } 295 296 /** 297 * Gets the directories to clean (if any). The returned array may contain null entries. 298 * 299 * @return The directories to clean or an empty array if none, never <code>null</code>. 300 */ 301 private Path[] getDirectories() { 302 Path[] directories; 303 if (excludeDefaultDirectories) { 304 directories = new Path[0]; 305 } else { 306 directories = new Path[] {directory, outputDirectory, testOutputDirectory, reportDirectory}; 307 } 308 return directories; 309 } 310 311 private Log getLog() { 312 return logger; 313 } 314 }