From 340d4f56394ea6f1d8f7831dd252d5a3f9bf0d8e Mon Sep 17 00:00:00 2001 From: Abdullah <89297042+AzazelSensei@users.noreply.github.com> Date: Thu, 27 Aug 2026 07:33:34 +0000 Subject: [PATCH] Skip creating a JAR when includes match no files skipIfEmpty only looked at whether the classes directory had any children. A test-jar with includes like **/junit/* still produced a manifest-only archive when those patterns matched nothing. Honor includes/excludes when deciding that the archive is empty. --- src/it/MJAR-510/pom.xml | 55 +++++++++++++++++++ .../src/main/java/my/org/app1/App1.java | 21 +++++++ .../src/test/java/my/org/app1/App1Test.java | 21 +++++++ src/it/MJAR-510/verify.bsh | 53 ++++++++++++++++++ .../maven/plugins/jar/AbstractJarMojo.java | 47 ++++++++-------- 5 files changed, 175 insertions(+), 22 deletions(-) create mode 100644 src/it/MJAR-510/pom.xml create mode 100644 src/it/MJAR-510/src/main/java/my/org/app1/App1.java create mode 100644 src/it/MJAR-510/src/test/java/my/org/app1/App1Test.java create mode 100644 src/it/MJAR-510/verify.bsh diff --git a/src/it/MJAR-510/pom.xml b/src/it/MJAR-510/pom.xml new file mode 100644 index 00000000..6e1fa3cf --- /dev/null +++ b/src/it/MJAR-510/pom.xml @@ -0,0 +1,55 @@ + + + + 4.0.0 + org.apache.maven.plugins + maven-jar-plugin-test-mjar-510 + 1.0 + jar + Maven + skipIfEmpty should skip when includes match no files + + + + org.apache.maven.plugins + maven-jar-plugin + @project.version@ + + + junit + + test-jar + + + junit + true + + **/junit/* + + + **/*Test.class + + + + + + + + diff --git a/src/it/MJAR-510/src/main/java/my/org/app1/App1.java b/src/it/MJAR-510/src/main/java/my/org/app1/App1.java new file mode 100644 index 00000000..965d4513 --- /dev/null +++ b/src/it/MJAR-510/src/main/java/my/org/app1/App1.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +package my.org.app1; + +public class App1 {} diff --git a/src/it/MJAR-510/src/test/java/my/org/app1/App1Test.java b/src/it/MJAR-510/src/test/java/my/org/app1/App1Test.java new file mode 100644 index 00000000..a25dbff0 --- /dev/null +++ b/src/it/MJAR-510/src/test/java/my/org/app1/App1Test.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +package my.org.app1; + +public class App1Test {} diff --git a/src/it/MJAR-510/verify.bsh b/src/it/MJAR-510/verify.bsh new file mode 100644 index 00000000..dc51ce8e --- /dev/null +++ b/src/it/MJAR-510/verify.bsh @@ -0,0 +1,53 @@ + +/* + * 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.*; + +try +{ + File target = new File( basedir, "target" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "target file is missing or not a directory." ); + return false; + } + + File artifact = new File( target, "maven-jar-plugin-test-mjar-510-1.0.jar" ); + if ( !artifact.exists() || artifact.isDirectory() ) + { + System.err.println( "main artifact is missing or a directory." ); + return false; + } + + File junitArtifact = new File( target, "maven-jar-plugin-test-mjar-510-1.0-junit.jar" ); + if ( junitArtifact.exists() ) + { + System.err.println( "junit classifier jar should not exist when includes match no files." ); + return false; + } + return true; +} +catch( Throwable e ) +{ + e.printStackTrace(); + return false; +} + +return false; diff --git a/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java b/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java index e3c91dfd..25c26d46 100644 --- a/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java +++ b/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java @@ -19,14 +19,12 @@ package org.apache.maven.plugins.jar; import java.io.File; -import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Map; import java.util.Objects; import java.util.jar.Attributes; -import java.util.stream.Stream; import org.apache.maven.api.ProducedArtifact; import org.apache.maven.api.Project; @@ -38,7 +36,6 @@ import org.apache.maven.api.services.ProjectManager; import org.apache.maven.shared.archiver.MavenArchiveConfiguration; import org.apache.maven.shared.archiver.MavenArchiver; -import org.apache.maven.shared.archiver.MavenArchiverException; import org.apache.maven.shared.model.fileset.FileSet; import org.apache.maven.shared.model.fileset.util.FileSetManager; import org.codehaus.plexus.archiver.Archiver; @@ -119,7 +116,7 @@ public abstract class AbstractJarMojo implements org.apache.maven.api.plugin.Moj * This plugin can not detect the post-processing, and so leaves the post-processed JAR file in place. * This can lead to failures when those plugins do not expect to find their own output as an input. * Set this parameter to {@code true} to recreate the JAR every time. - * When {@link #skipIfEmpty} is {@code true} and the classes directory is empty, packaging is skipped even if + * When {@link #skipIfEmpty} is {@code true} and no files would be included, packaging is skipped even if * {@code forceCreation} is true. * *

Starting with 3.0.0 the property has been renamed from {@code jar.forceCreation} @@ -130,8 +127,8 @@ public abstract class AbstractJarMojo implements org.apache.maven.api.plugin.Moj /** * Skip creating empty archives. - * When {@code true}, packaging is skipped if the classes directory is empty, even if {@link #forceCreation} is also - * {@code true}. + * When {@code true}, packaging is skipped if the classes directory is empty or if no files match the configured + * includes/excludes, even if {@link #forceCreation} is also {@code true}. */ @Parameter(defaultValue = "false") protected boolean skipIfEmpty; @@ -239,13 +236,7 @@ public Path createArchive() throws MojoException { finalName != null ? finalName : project.getBuild().getFinalName(); Path jarFile = getJarFile(basedir, resultFinalName, getClassifier()); - FileSetManager fileSetManager = new FileSetManager(); - FileSet jarContentFileSet = new FileSet(); - jarContentFileSet.setDirectory(getClassesDirectory().toAbsolutePath().toString()); - jarContentFileSet.setIncludes(Arrays.asList(getIncludes())); - jarContentFileSet.setExcludes(Arrays.asList(getExcludes())); - - String[] includedFiles = fileSetManager.getIncludedFiles(jarContentFileSet); + String[] includedFiles = getIncludedFiles(); if (detectMultiReleaseJar && Arrays.stream(includedFiles) @@ -305,7 +296,7 @@ public Path createArchive() throws MojoException { */ @Override public void execute() throws MojoException { - if (skipIfEmpty && isEmpty(getClassesDirectory())) { + if (skipIfEmpty && hasNoFilesToInclude()) { getLog().info(String.format("Skipping packaging of the %s.", getType())); } else { Path jarFile = createArchive(); @@ -335,15 +326,27 @@ public void execute() throws MojoException { } } - private static boolean isEmpty(Path directory) { - if (!Files.isDirectory(directory)) { - return true; - } - try (Stream children = Files.list(directory)) { - return children.findAny().isEmpty(); - } catch (IOException e) { - throw new MavenArchiverException("Unable to access directory", e); + /** + * {@return true} if the classes directory is missing or if includes/excludes select no files. + */ + private boolean hasNoFilesToInclude() { + return getIncludedFiles().length == 0; + } + + /** + * Files under the classes directory that match {@link #includes} and {@link #excludes}. + */ + private String[] getIncludedFiles() { + Path classesDirectory = getClassesDirectory(); + if (!Files.isDirectory(classesDirectory)) { + return new String[0]; } + FileSetManager fileSetManager = new FileSetManager(); + FileSet jarContentFileSet = new FileSet(); + jarContentFileSet.setDirectory(classesDirectory.toAbsolutePath().toString()); + jarContentFileSet.setIncludes(Arrays.asList(getIncludes())); + jarContentFileSet.setExcludes(Arrays.asList(getExcludes())); + return fileSetManager.getIncludedFiles(jarContentFileSet); } private boolean projectHasAlreadySetAnArtifact() {