-
Notifications
You must be signed in to change notification settings - Fork 56
SpotlessApply uses by default the Java formatter #1243
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
Changes from 6 commits
d44a3fa
e4a42d1
070c5cf
17e9bdd
6021843
66c2d7c
b9b5073
81ff9ae
9cd9bc2
ea77c75
7d30bfb
065d9c5
113ee73
1dfcc88
22132dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -182,6 +182,25 @@ shortcut. | |
|
||
 | ||
|
||
## Java 21 Support | ||
|
||
In [1211](https://github.com/palantir/palantir-java-format/pull/1211) we shipped Java 21 support. In order to use the | ||
Java 21 formatting capabilities, ensure that either: | ||
|
||
- the Gradle daemon and the Intellij Project SDK are set to Java 21 | ||
- or that the gradle property `palantir.native.formatter=true`. This will run the formatter as a native image, | ||
- independent of the Gradle daemon/Intellij project JDK version. | ||
|
||
### Native image formatter | ||
|
||
[This comment](https://github.com/palantir/palantir-java-format/issues/952#issuecomment-2575750610) explains why we | ||
switched to a native image for the formatter. The startup time for the native image esp. in Intellij is >10x faster than | ||
spinning up a new process that does the formatting. | ||
However, the throughput of running the native image for a large set of files (eg. running `./gradlew spotlessApply`) is | ||
considerably slower (eg. 30ms using the Java implementation vs 1m20s using the native image implementation). Therefore, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 30 milliseconds vs 1 minute 20s? |
||
when running the formatter from `spotlessApply` we will default to using the Java implementation (if the Java version >= 21). | ||
|
||
|
||
## Future works | ||
|
||
- [ ] preserve [NON-NLS markers][] - these are comments that are used when implementing NLS internationalisation, and need to stay on the same line with the strings they come after. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
type: fix | ||
fix: | ||
description: SpotlessApply uses Java 21 version | ||
links: | ||
- https://github.com/palantir/palantir-java-format/pull/1243 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,11 @@ package com.palantir.javaformat.gradle | |
import nebula.test.IntegrationTestKitSpec | ||
import spock.lang.Unroll | ||
|
||
import java.nio.charset.StandardCharsets | ||
import java.nio.file.Path | ||
import java.util.stream.Collectors | ||
import java.util.stream.Stream | ||
|
||
class PalantirJavaFormatSpotlessPluginTest extends IntegrationTestKitSpec { | ||
/** ./gradlew writeImplClasspath generates this file. */ | ||
private static final CLASSPATH_FILE = new File("build/impl.classpath").absolutePath | ||
|
@@ -26,20 +31,61 @@ class PalantirJavaFormatSpotlessPluginTest extends IntegrationTestKitSpec { | |
|
||
|
||
@Unroll | ||
def "formats with spotless when spotless is applied"(String extraGradleProperties, String expectedOutput) { | ||
def "formats with spotless when spotless is applied"(String extraGradleProperties, String javaVersion, String expectedOutput) { | ||
def extraDependencies = extraGradleProperties.isEmpty() ? "" : NATIVE_CONFIG | ||
settingsFile << """ | ||
buildscript { | ||
repositories { | ||
mavenCentral() { metadataSources { mavenPom(); ignoreGradleMetadataRedirection() } } | ||
gradlePluginPortal() { metadataSources { mavenPom(); ignoreGradleMetadataRedirection() } } | ||
} | ||
dependencies { | ||
classpath 'com.palantir.gradle.jdks:gradle-jdks-settings:0.62.0' | ||
} | ||
} | ||
apply plugin: 'com.palantir.jdks.settings' | ||
""".stripIndent(true) | ||
|
||
buildFile << """ | ||
buildscript { | ||
repositories { | ||
mavenCentral() { metadataSources { mavenPom(); ignoreGradleMetadataRedirection() } } | ||
gradlePluginPortal() { metadataSources { mavenPom(); ignoreGradleMetadataRedirection() } } | ||
} | ||
dependencies { | ||
classpath 'com.palantir.baseline:gradle-baseline-java:6.21.0' | ||
classpath 'com.palantir.gradle.jdks:gradle-jdks:0.62.0' | ||
classpath 'com.palantir.gradle.jdkslatest:gradle-jdks-latest:0.17.0' | ||
classpath files(FILES) | ||
} | ||
} | ||
|
||
// The 'com.diffplug.spotless:spotless-plugin-gradle' dependency is already added by palantir-java-format | ||
plugins { | ||
id 'java' | ||
id 'com.palantir.java-format' | ||
} | ||
|
||
apply plugin: 'com.palantir.java-format' | ||
apply plugin: 'com.palantir.baseline-java-versions' | ||
apply plugin: 'com.palantir.jdks' | ||
apply plugin: 'com.palantir.jdks.latest' | ||
|
||
dependencies { | ||
palantirJavaFormat files(file("${CLASSPATH_FILE}").text.split(':')) | ||
EXTRA_CONFIGURATION | ||
} | ||
""".replace("EXTRA_CONFIGURATION", extraDependencies).stripIndent() | ||
|
||
javaVersions { | ||
libraryTarget = JAVA_VERSION | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we just string interpolate here? |
||
} | ||
|
||
jdks { | ||
daemonTarget = JAVA_VERSION | ||
} | ||
|
||
""".replace("FILES", getBuildPluginClasspathInjector().join(",")) | ||
.replace("JAVA_VERSION", javaVersion) | ||
.replace("EXTRA_CONFIGURATION", extraDependencies).stripIndent() | ||
|
||
// Add jvm args to allow spotless and formatter gradle plugins to run with Java 16+ | ||
file('gradle.properties') << """ | ||
|
@@ -48,6 +94,7 @@ class PalantirJavaFormatSpotlessPluginTest extends IntegrationTestKitSpec { | |
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ | ||
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ | ||
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED | ||
palantir.jdk.setup.enabled=true | ||
""".stripIndent() | ||
file('gradle.properties') << extraGradleProperties | ||
|
||
|
@@ -56,19 +103,58 @@ class PalantirJavaFormatSpotlessPluginTest extends IntegrationTestKitSpec { | |
""".stripIndent() | ||
|
||
file('src/main/java/Main.java').text = invalidJavaFile | ||
runTasks('wrapper') | ||
|
||
when: | ||
def result = runTasks('spotlessApply', '--info') | ||
def result = runGradlewTasks('spotlessApply', '--info') | ||
|
||
then: | ||
result.output.contains(expectedOutput) | ||
result.contains(expectedOutput) | ||
file('src/main/java/Main.java').text == validJavaFile | ||
|
||
where: | ||
extraGradleProperties | expectedOutput | ||
"" | "Using the legacy palantir-java-formatter" | ||
"palantir.native.formatter=true" | "Using the native-image" | ||
extraGradleProperties | javaVersion | expectedOutput | ||
"" | 21 | "Using the Java-based formatter" | ||
"palantir.native.formatter=true" | 21 | "Using the Java-based formatter" | ||
"palantir.native.formatter=true" | 17 | "Using the native-image formatter" | ||
|
||
} | ||
|
||
private static Iterable<File> getBuildPluginClasspathInjector() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should I move this to https://github.com/palantir/gradle-plugin-testing ? gradle-jdks needs to do the same thing in order to run on multiple Java versions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand what it's supposed to do? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose what I don't understand is why you need this at all? I thought nebula does this for you? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I need to test how the task works against Java 17 and Java 21. So, I am using the I have the same setup in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh, I thought the tooling api invoked gradlew, interesting! you could move it to gradle-plugin-testing, it's a bit niche, but I don't see why not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a nicer way to do this, is probably to reuse some of the stuff in Nebula, it makes your life a lot easier I think:
I think the current approach - I also mentioned something similar in the testing plugin PRs - detracts from the test by adding a bunch of cruft that's not actually important to know for the test, but is unfortunately required for the test to pass. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It also means, that you can hide all of this behind a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An aside: I think you could probably ship this method + |
||
return getPluginClasspathInjector(Path.of("../gradle-palantir-java-format/build/pluginUnderTestMetadata/plugin-under-test-metadata.properties")) | ||
} | ||
|
||
private static Iterable<File> getPluginClasspathInjector(Path path) { | ||
File propertiesFile = path.toFile() | ||
Properties properties = new Properties() | ||
propertiesFile.withInputStream { inputStream -> | ||
properties.load(inputStream) | ||
} | ||
String classpath = properties.getProperty('implementation-classpath') | ||
return classpath.split(File.pathSeparator).collect { "'" + it + "'" } | ||
} | ||
|
||
private String runGradlewTasks(String... tasks) { | ||
ProcessBuilder processBuilder = getProcessBuilder(tasks) | ||
Process process = processBuilder.start() | ||
String output = readAllInput(process.getInputStream()) | ||
return output | ||
} | ||
|
||
public static String readAllInput(InputStream inputStream) { | ||
try (Stream<String> lines = | ||
new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines()) { | ||
return lines.collect(Collectors.joining("\n")); | ||
} | ||
} | ||
|
||
private ProcessBuilder getProcessBuilder(String... tasks) { | ||
List<String> arguments = ["./gradlew"] | ||
Arrays.asList(tasks).forEach(arguments::add) | ||
ProcessBuilder processBuilder = new ProcessBuilder() | ||
.command(arguments) | ||
.directory(projectDir).redirectErrorStream(true) | ||
return processBuilder | ||
} | ||
|
||
def validJavaFile = '''\ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a tad confusing, in both cases you're spinning up a new process.
Correct me if I've misunderstood, but I believe you want something along the lines of:
?