Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/it/sources-targetPath/outside-target/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!---
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.
-->

The purpose of this file is to verify that the directory specified by `<targetPath>` is deleted.
This verification requires a directory outside the `target` directory, otherwise its deletion could
not be distinguished from the usual deletion of `${maven.build.directory}`.
46 changes: 46 additions & 0 deletions src/it/sources-targetPath/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 http://maven.apache.org/xsd/maven-4.1.0.xsd">
<modelVersion>4.1.0</modelVersion>

<groupId>test</groupId>
<artifactId>sources-targetPath</artifactId>
<version>1.0-SNAPSHOT</version>

<name>Test &lt;targetPath&gt; cleaning</name>
<description>Check for proper cleaning of output files in directories specified by &lt;targetPath&gt;.</description>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>@pom.version@</version>
</plugin>
</plugins>

<sources>
<source>
<targetPath>${project.basedir}/outside-target</targetPath>
</source>
</sources>
</build>

</project>
23 changes: 23 additions & 0 deletions src/it/sources-targetPath/setup.bsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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.
*/

import java.io.File;

return new File(basedir, "target").exists() && new File(basedir, "outside-target").exists();
// The opposite condition is tested in "verify.bsh".
19 changes: 19 additions & 0 deletions src/it/sources-targetPath/target/test-classes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!---
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.
-->

The purpose of this file is to verify that the directory
specified by `<testOutputDirectory>` is still deleted.
22 changes: 22 additions & 0 deletions src/it/sources-targetPath/verify.bsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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.
*/

import java.io.File;

return !(new File(basedir, "target").exists() || new File(basedir, "outside-target").exists());
111 changes: 85 additions & 26 deletions src/main/java/org/apache/maven/plugins/clean/CleanMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,21 @@

import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import org.apache.maven.api.Project;
import org.apache.maven.api.ProjectScope;
import org.apache.maven.api.Session;
import org.apache.maven.api.di.Inject;
import org.apache.maven.api.model.Build;
import org.apache.maven.api.plugin.Log;
import org.apache.maven.api.plugin.MojoException;
import org.apache.maven.api.plugin.annotations.Mojo;
import org.apache.maven.api.plugin.annotations.Parameter;
import org.apache.maven.api.services.ProjectManager;

/**
* Goal which cleans the build.
Expand All @@ -52,35 +60,32 @@ public class CleanMojo implements org.apache.maven.api.plugin.Mojo {

public static final String FAST_MODE_DEFER = "defer";

/**
* The logger where to send information about what the plugin is doing.
*/
@Inject
private Log logger;

/**
* This is where build results go.
* The directory where build results went.
*/
@Parameter(defaultValue = "${project.build.directory}", readonly = true, required = true)
private Path directory;

/**
* This is where compiled classes go.
* The directory where compiled main classes went. This is usually a sub-directory
* of {@link #directory}, but could be configured by the user to another location.
*/
@Parameter(defaultValue = "${project.build.outputDirectory}", readonly = true, required = true)
private Path outputDirectory;

/**
* This is where compiled test classes go.
* The directory where compiled test classes went. This is usually a sub-directory
* of {@link #directory}, but could be configured by the user to another location.
*/
@Parameter(defaultValue = "${project.build.testOutputDirectory}", readonly = true, required = true)
private Path testOutputDirectory;

/**
* This is where the site plugin generates its pages.
*
* @since 2.1.1
*/
@Parameter(defaultValue = "${project.build.outputDirectory}", readonly = true, required = true)
private Path reportDirectory;

/**
* Sets whether the plugin runs in verbose mode. As of plugin version 2.3, the default value is derived from Maven's
* global debug flag (compare command line switch {@code -X}).
Expand Down Expand Up @@ -218,15 +223,34 @@ public class CleanMojo implements org.apache.maven.api.plugin.Mojo {
@Parameter(property = "maven.clean.fastMode", defaultValue = FAST_MODE_BACKGROUND)
private String fastMode;

/**
* The current session. May be null during some tests.
*/
@Inject
private Session session;

/**
* Deletes file-sets in the following project build directory order: (source) directory, output directory, test
* directory, report directory, and then the additional file-sets.
* The current project instance.
*/
@Inject
private Project project;

/**
* Deletes build directories and file-sets.
* Directories are deleted in the following order:
*
* @throws MojoException When a directory failed to get deleted.
* @see org.apache.maven.api.plugin.Mojo#execute()
* <ol>
* <li>Build directory ({@code ${project.build.directory}})</li>
* <li>Main classes directory ({@code ${project.build.outputDirectory}})</li>
* <li>Test classes directory ({@code ${project.build.testOutputDirectory}})</li>
* <li>Directories specified in the {@code <targetPath>} child of {@code <source>} elements</li>
* <li>Additional file-sets</li>
* </ol>
*
* Redundant directories are omitted. For example, the main classes directory will be skipped
* in the usual case where it is a sub-directory of the build directory.
*
* @throws MojoException if a directory cannot be deleted
*/
@Override
public void execute() {
Expand Down Expand Up @@ -263,9 +287,7 @@ public void execute() {
session, logger, isVerbose(), fastDir, fastMode, followSymLinks, force, failOnError, retryOnError);
try {
for (Path directoryItem : getDirectories()) {
if (directoryItem != null) {
cleaner.delete(directoryItem);
}
cleaner.delete(directoryItem);
}
if (filesets != null) {
for (Fileset fileset : filesets) {
Expand All @@ -290,16 +312,53 @@ private boolean isVerbose() {
}

/**
* Gets the directories to clean (if any). The returned array may contain null entries.
*
* @return The directories to clean or an empty array if none, never {@code null}.
* {@return the default directories to clean, or an empty list if none}
* The list includes the directories specified in both the Maven 3 and Maven 4 ways.
* Null items and redundant directories (children of other items) are omitted.
*/
private Path[] getDirectories() {
Path[] directories;
private List<Path> getDirectories() {
if (excludeDefaultDirectories) {
directories = new Path[0];
} else {
directories = new Path[] {directory, outputDirectory, testOutputDirectory, reportDirectory};
return List.of();
}

// Directories declared in the Maven 3 way.
var directories = new ArrayList<Path>(Arrays.asList(directory, outputDirectory, testOutputDirectory));

// Directories declared in the Maven 4 way, with <source> elements.
if (project != null && session != null) {
ProjectManager projectManager = session.getService(ProjectManager.class);
if (projectManager != null) {
final Build build = project.getBuild();
projectManager.getSourceRoots(project).forEach((source) -> {
source.targetPath().ifPresent((targetPath) -> {
// TODO: we can replace by `Project.getOutputDirectory(scope)` if MVN-11322 is merged.
String base;
ProjectScope scope = source.scope();
if (scope == ProjectScope.MAIN) {
base = build.getOutputDirectory();
} else if (scope == ProjectScope.TEST) {
base = build.getTestOutputDirectory();
} else {
base = build.getDirectory();
}
Path dir = project.getBasedir().resolve(base);
directories.add(dir.resolve(targetPath));
});
});
}
}
/*
* Remove children of included parents. In the common case where the first element in the list is
* the parent of all other directories, these loops will do only one iteration over all elements.
*/
directories.removeIf(Objects::isNull);
for (int i = 0; i < directories.size(); i++) {
final Path prefix = directories.get(i);
for (int j = directories.size(); --j >= 0; ) {
if (j != i && directories.get(j).startsWith(prefix)) {
directories.remove(j); // Keep only the base directories.
}
}
}
return directories;
}
Expand Down
Loading