Skip to content

Commit a6b8083

Browse files
nosansnicoll
authored andcommitted
Use ArgFile for classpath argument on Windows
This commit uses @argfile syntax for classpath argument on Windows OS to avoid creating a command-line that is too long. See gh-44305 Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
1 parent 0a42082 commit a6b8083

File tree

8 files changed

+322
-109
lines changed

8 files changed

+322
-109
lines changed

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractAotMojo.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -46,8 +46,6 @@
4646
import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
4747
import org.apache.maven.toolchain.ToolchainManager;
4848

49-
import org.springframework.boot.maven.CommandLineBuilder.ClasspathBuilder;
50-
5149
/**
5250
* Abstract base class for AOT processing MOJOs.
5351
*
@@ -149,7 +147,7 @@ protected final void compileSourceFiles(URL[] classPath, File sourcesDirectory,
149147
JavaCompilerPluginConfiguration compilerConfiguration = new JavaCompilerPluginConfiguration(this.project);
150148
List<String> options = new ArrayList<>();
151149
options.add("-cp");
152-
options.add(ClasspathBuilder.build(Arrays.asList(classPath)));
150+
options.add(ClasspathBuilder.build(classPath));
153151
options.add("-d");
154152
options.add(outputDirectory.toPath().toAbsolutePath().toString());
155153
String releaseVersion = compilerConfiguration.getReleaseVersion();

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java

+3-69
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,15 +20,10 @@
2020
import java.io.IOException;
2121
import java.net.MalformedURLException;
2222
import java.net.URL;
23-
import java.nio.charset.Charset;
24-
import java.nio.charset.UnsupportedCharsetException;
25-
import java.nio.file.Files;
26-
import java.nio.file.Path;
2723
import java.util.ArrayList;
2824
import java.util.Arrays;
2925
import java.util.Collections;
3026
import java.util.List;
31-
import java.util.Locale;
3227
import java.util.Map;
3328
import java.util.Set;
3429
import java.util.stream.Collectors;
@@ -46,7 +41,6 @@
4641
import org.springframework.boot.loader.tools.FileUtils;
4742
import org.springframework.util.Assert;
4843
import org.springframework.util.ObjectUtils;
49-
import org.springframework.util.StringUtils;
5044

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

352346
private void addClasspath(List<String> args) throws MojoExecutionException {
353347
try {
354-
StringBuilder classpath = new StringBuilder();
355-
for (URL ele : getClassPathUrls()) {
356-
if (!classpath.isEmpty()) {
357-
classpath.append(File.pathSeparator);
358-
}
359-
classpath.append(new File(ele.toURI()));
360-
}
348+
String classpath = ClasspathBuilder.build(getClassPathUrls());
361349
if (getLog().isDebugEnabled()) {
362350
getLog().debug("Classpath for forked process: " + classpath);
363351
}
364352
args.add("-cp");
365-
if (needsClasspathArgFile()) {
366-
args.add("@" + ArgFile.create(classpath).path());
367-
}
368-
else {
369-
args.add(classpath.toString());
370-
}
353+
args.add(classpath);
371354
}
372355
catch (Exception ex) {
373356
throw new MojoExecutionException("Could not build classpath", ex);
374357
}
375358
}
376359

377-
private boolean needsClasspathArgFile() {
378-
// Windows limits the maximum command length, so we use an argfile there
379-
return runsOnWindows();
380-
}
381-
382-
private boolean runsOnWindows() {
383-
String os = System.getProperty("os.name");
384-
if (!StringUtils.hasLength(os)) {
385-
if (getLog().isWarnEnabled()) {
386-
getLog().warn("System property os.name is not set");
387-
}
388-
return false;
389-
}
390-
return os.toLowerCase(Locale.ROOT).contains("win");
391-
}
392-
393360
protected URL[] getClassPathUrls() throws MojoExecutionException {
394361
try {
395362
List<URL> urls = new ArrayList<>();
@@ -468,37 +435,4 @@ static String format(String key, String value) {
468435

469436
}
470437

471-
record ArgFile(Path path) {
472-
473-
private void write(CharSequence content) throws IOException {
474-
Files.writeString(this.path, "\"" + escape(content) + "\"", getCharset());
475-
}
476-
477-
private Charset getCharset() {
478-
String nativeEncoding = System.getProperty("native.encoding");
479-
if (nativeEncoding == null) {
480-
return Charset.defaultCharset();
481-
}
482-
try {
483-
return Charset.forName(nativeEncoding);
484-
}
485-
catch (UnsupportedCharsetException ex) {
486-
return Charset.defaultCharset();
487-
}
488-
}
489-
490-
private String escape(CharSequence content) {
491-
return content.toString().replace("\\", "\\\\");
492-
}
493-
494-
static ArgFile create(CharSequence content) throws IOException {
495-
Path tempFile = Files.createTempFile("spring-boot-", ".argfile");
496-
tempFile.toFile().deleteOnExit();
497-
ArgFile argFile = new ArgFile(tempFile);
498-
argFile.write(content);
499-
return argFile;
500-
}
501-
502-
}
503-
504438
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.maven;
18+
19+
import java.io.IOException;
20+
import java.nio.charset.Charset;
21+
import java.nio.charset.UnsupportedCharsetException;
22+
import java.nio.file.Files;
23+
import java.nio.file.Path;
24+
25+
/**
26+
*
27+
* Utility class that represents `argument` as a file. Mostly used to avoid `Path too long
28+
* on ...` on Windows.
29+
*
30+
* @author Moritz Halbritter
31+
* @author Dmytro Nosan
32+
*/
33+
final class ArgFile {
34+
35+
private final Path path;
36+
37+
private ArgFile(Path path) {
38+
this.path = path.toAbsolutePath();
39+
}
40+
41+
/**
42+
* Creates a new {@code ArgFile} with the given content.
43+
* @param content the content to write to the argument file
44+
* @return a new {@code ArgFile}
45+
* @throws IOException if an I/O error occurs
46+
*/
47+
static ArgFile create(CharSequence content) throws IOException {
48+
Path tempFile = Files.createTempFile("spring-boot-", ".argfile");
49+
tempFile.toFile().deleteOnExit();
50+
ArgFile argFile = new ArgFile(tempFile);
51+
argFile.write(content);
52+
return argFile;
53+
}
54+
55+
private void write(CharSequence content) throws IOException {
56+
Files.writeString(this.path, "\"" + escape(content) + "\"", getCharset());
57+
}
58+
59+
private Charset getCharset() {
60+
String nativeEncoding = System.getProperty("native.encoding");
61+
if (nativeEncoding == null) {
62+
return Charset.defaultCharset();
63+
}
64+
try {
65+
return Charset.forName(nativeEncoding);
66+
}
67+
catch (UnsupportedCharsetException ex) {
68+
return Charset.defaultCharset();
69+
}
70+
}
71+
72+
private String escape(CharSequence content) {
73+
return content.toString().replace("\\", "\\\\");
74+
}
75+
76+
@Override
77+
public String toString() {
78+
return this.path.toString();
79+
}
80+
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.maven;
18+
19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.net.URISyntaxException;
22+
import java.net.URL;
23+
import java.util.Locale;
24+
25+
import org.springframework.util.ObjectUtils;
26+
import org.springframework.util.StringUtils;
27+
28+
/**
29+
* Helper class to build the -cp (classpath) argument of a java process.
30+
*
31+
* @author Stephane Nicoll
32+
* @author Dmytro Nosan
33+
*/
34+
final class ClasspathBuilder {
35+
36+
private ClasspathBuilder() {
37+
}
38+
39+
/**
40+
* Builds a classpath string or an argument file representing the classpath, depending
41+
* on the operating system.
42+
* @param urls an array of {@link URL} representing the elements of the classpath
43+
* @return the classpath; on Windows, the path to an argument file is returned,
44+
* prefixed with '@'
45+
*/
46+
static String build(URL... urls) {
47+
if (ObjectUtils.isEmpty(urls)) {
48+
return "";
49+
}
50+
if (urls.length == 1) {
51+
return toFile(urls[0]).toString();
52+
}
53+
StringBuilder builder = new StringBuilder();
54+
for (URL url : urls) {
55+
if (!builder.isEmpty()) {
56+
builder.append(File.pathSeparator);
57+
}
58+
builder.append(toFile(url));
59+
}
60+
String classpath = builder.toString();
61+
if (runsOnWindows()) {
62+
try {
63+
return "@" + ArgFile.create(classpath);
64+
}
65+
catch (IOException ex) {
66+
return classpath;
67+
}
68+
}
69+
return classpath;
70+
}
71+
72+
private static File toFile(URL url) {
73+
try {
74+
return new File(url.toURI());
75+
}
76+
catch (URISyntaxException ex) {
77+
throw new IllegalArgumentException(ex);
78+
}
79+
}
80+
81+
private static boolean runsOnWindows() {
82+
String os = System.getProperty("os.name");
83+
if (!StringUtils.hasText(os)) {
84+
return false;
85+
}
86+
return os.toLowerCase(Locale.ROOT).contains("win");
87+
}
88+
89+
}

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/CommandLineBuilder.java

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

1717
package org.springframework.boot.maven;
1818

19-
import java.io.File;
20-
import java.net.URISyntaxException;
2119
import java.net.URL;
2220
import java.util.ArrayList;
2321
import java.util.Arrays;
@@ -84,7 +82,7 @@ List<String> build() {
8482
}
8583
if (!this.classpathElements.isEmpty()) {
8684
commandLine.add("-cp");
87-
commandLine.add(ClasspathBuilder.build(this.classpathElements));
85+
commandLine.add(ClasspathBuilder.build(this.classpathElements.toArray(URL[]::new)));
8886
}
8987
commandLine.add(this.mainClass);
9088
if (!this.arguments.isEmpty()) {
@@ -93,30 +91,6 @@ List<String> build() {
9391
return commandLine;
9492
}
9593

96-
static class ClasspathBuilder {
97-
98-
static String build(List<URL> classpathElements) {
99-
StringBuilder classpath = new StringBuilder();
100-
for (URL element : classpathElements) {
101-
if (!classpath.isEmpty()) {
102-
classpath.append(File.pathSeparator);
103-
}
104-
classpath.append(toFile(element));
105-
}
106-
return classpath.toString();
107-
}
108-
109-
private static File toFile(URL element) {
110-
try {
111-
return new File(element.toURI());
112-
}
113-
catch (URISyntaxException ex) {
114-
throw new IllegalArgumentException(ex);
115-
}
116-
}
117-
118-
}
119-
12094
/**
12195
* Format System properties.
12296
*/

0 commit comments

Comments
 (0)