Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maven plugin does not consistently use ArgFile for classpath argument on Windows #44305

Closed
wants to merge 1 commit into from
Closed
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,8 +46,6 @@
import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
import org.apache.maven.toolchain.ToolchainManager;

import org.springframework.boot.maven.CommandLineBuilder.ClasspathBuilder;

/**
* Abstract base class for AOT processing MOJOs.
*
@@ -149,7 +147,7 @@ protected final void compileSourceFiles(URL[] classPath, File sourcesDirectory,
JavaCompilerPluginConfiguration compilerConfiguration = new JavaCompilerPluginConfiguration(this.project);
List<String> options = new ArrayList<>();
options.add("-cp");
options.add(ClasspathBuilder.build(Arrays.asList(classPath)));
options.add(ClasspathBuilder.build(classPath));
options.add("-d");
options.add(outputDirectory.toPath().toAbsolutePath().toString());
String releaseVersion = compilerConfiguration.getReleaseVersion();
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,15 +20,10 @@
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -46,7 +41,6 @@
import org.springframework.boot.loader.tools.FileUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
* Base class to run a Spring Boot application.
@@ -351,45 +345,18 @@ private void addActiveProfileArgument(RunArguments arguments) {

private void addClasspath(List<String> args) throws MojoExecutionException {
try {
StringBuilder classpath = new StringBuilder();
for (URL ele : getClassPathUrls()) {
if (!classpath.isEmpty()) {
classpath.append(File.pathSeparator);
}
classpath.append(new File(ele.toURI()));
}
String classpath = ClasspathBuilder.build(getClassPathUrls());
if (getLog().isDebugEnabled()) {
getLog().debug("Classpath for forked process: " + classpath);
}
args.add("-cp");
if (needsClasspathArgFile()) {
args.add("@" + ArgFile.create(classpath).path());
}
else {
args.add(classpath.toString());
}
args.add(classpath);
}
catch (Exception ex) {
throw new MojoExecutionException("Could not build classpath", ex);
}
}

private boolean needsClasspathArgFile() {
// Windows limits the maximum command length, so we use an argfile there
return runsOnWindows();
}

private boolean runsOnWindows() {
String os = System.getProperty("os.name");
if (!StringUtils.hasLength(os)) {
if (getLog().isWarnEnabled()) {
getLog().warn("System property os.name is not set");
}
return false;
}
return os.toLowerCase(Locale.ROOT).contains("win");
}

protected URL[] getClassPathUrls() throws MojoExecutionException {
try {
List<URL> urls = new ArrayList<>();
@@ -468,37 +435,4 @@ static String format(String key, String value) {

}

record ArgFile(Path path) {

private void write(CharSequence content) throws IOException {
Files.writeString(this.path, "\"" + escape(content) + "\"", getCharset());
}

private Charset getCharset() {
String nativeEncoding = System.getProperty("native.encoding");
if (nativeEncoding == null) {
return Charset.defaultCharset();
}
try {
return Charset.forName(nativeEncoding);
}
catch (UnsupportedCharsetException ex) {
return Charset.defaultCharset();
}
}

private String escape(CharSequence content) {
return content.toString().replace("\\", "\\\\");
}

static ArgFile create(CharSequence content) throws IOException {
Path tempFile = Files.createTempFile("spring-boot-", ".argfile");
tempFile.toFile().deleteOnExit();
ArgFile argFile = new ArgFile(tempFile);
argFile.write(content);
return argFile;
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2012-2025 the original author or authors.
*
* Licensed 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
*
* https://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 org.springframework.boot.maven;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.nio.file.Files;
import java.nio.file.Path;

/**
*
* Utility class that represents `argument` as a file. Mostly used to avoid `Path too long
* on ...` on Windows.
*
* @author Moritz Halbritter
* @author Dmytro Nosan
*/
final class ArgFile {

private final Path path;

private ArgFile(Path path) {
this.path = path.toAbsolutePath();
}

/**
* Creates a new {@code ArgFile} with the given content.
* @param content the content to write to the argument file
* @return a new {@code ArgFile}
* @throws IOException if an I/O error occurs
*/
static ArgFile create(CharSequence content) throws IOException {
Path tempFile = Files.createTempFile("spring-boot-", ".argfile");
tempFile.toFile().deleteOnExit();
ArgFile argFile = new ArgFile(tempFile);
argFile.write(content);
return argFile;
}

private void write(CharSequence content) throws IOException {
Files.writeString(this.path, "\"" + escape(content) + "\"", getCharset());
}

private Charset getCharset() {
String nativeEncoding = System.getProperty("native.encoding");
if (nativeEncoding == null) {
return Charset.defaultCharset();
}
try {
return Charset.forName(nativeEncoding);
}
catch (UnsupportedCharsetException ex) {
return Charset.defaultCharset();
}
}

private String escape(CharSequence content) {
return content.toString().replace("\\", "\\\\");
}

@Override
public String toString() {
return this.path.toString();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright 2012-2025 the original author or authors.
*
* Licensed 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
*
* https://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 org.springframework.boot.maven;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Locale;

import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
* Helper class to build the -cp (classpath) argument of a java process.
*
* @author Stephane Nicoll
* @author Dmytro Nosan
*/
final class ClasspathBuilder {

private ClasspathBuilder() {
}

/**
* Builds a classpath string or an argument file representing the classpath, depending
* on the operating system.
* @param urls an array of {@link URL} representing the elements of the classpath
* @return the classpath; on Windows, the path to an argument file is returned,
* prefixed with '@'
*/
static String build(URL... urls) {
if (ObjectUtils.isEmpty(urls)) {
return "";
}
if (urls.length == 1) {
return toFile(urls[0]).toString();
}
StringBuilder builder = new StringBuilder();
for (URL url : urls) {
if (!builder.isEmpty()) {
builder.append(File.pathSeparator);
}
builder.append(toFile(url));
}
String classpath = builder.toString();
if (runsOnWindows()) {
try {
return "@" + ArgFile.create(classpath);
}
catch (IOException ex) {
return classpath;
}
}
return classpath;
}

private static File toFile(URL url) {
try {
return new File(url.toURI());
}
catch (URISyntaxException ex) {
throw new IllegalArgumentException(ex);
}
}

private static boolean runsOnWindows() {
String os = System.getProperty("os.name");
if (!StringUtils.hasText(os)) {
return false;
}
return os.toLowerCase(Locale.ROOT).contains("win");
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +16,6 @@

package org.springframework.boot.maven;

import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
@@ -84,7 +82,7 @@ List<String> build() {
}
if (!this.classpathElements.isEmpty()) {
commandLine.add("-cp");
commandLine.add(ClasspathBuilder.build(this.classpathElements));
commandLine.add(ClasspathBuilder.build(this.classpathElements.toArray(URL[]::new)));
}
commandLine.add(this.mainClass);
if (!this.arguments.isEmpty()) {
@@ -93,30 +91,6 @@ List<String> build() {
return commandLine;
}

static class ClasspathBuilder {

static String build(List<URL> classpathElements) {
StringBuilder classpath = new StringBuilder();
for (URL element : classpathElements) {
if (!classpath.isEmpty()) {
classpath.append(File.pathSeparator);
}
classpath.append(toFile(element));
}
return classpath.toString();
}

private static File toFile(URL element) {
try {
return new File(element.toURI());
}
catch (URISyntaxException ex) {
throw new IllegalArgumentException(ex);
}
}

}

/**
* Format System properties.
*/
Loading