diff --git a/changelog/@unreleased/pr-1313.v2.yml b/changelog/@unreleased/pr-1313.v2.yml new file mode 100644 index 000000000..169df36fd --- /dev/null +++ b/changelog/@unreleased/pr-1313.v2.yml @@ -0,0 +1,6 @@ +type: improvement +improvement: + description: Make palantir-java-format support spotless 6.22.0, which is [transitively + brought in from baseline to support the configuration cache](https://github.com/palantir/gradle-baseline/pull/3119) + links: + - https://github.com/palantir/palantir-java-format/pull/1313 diff --git a/gradle-palantir-java-format/src/main/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatPlugin.java b/gradle-palantir-java-format/src/main/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatPlugin.java index 93709e61b..1a26a4c03 100644 --- a/gradle-palantir-java-format/src/main/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatPlugin.java +++ b/gradle-palantir-java-format/src/main/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatPlugin.java @@ -17,21 +17,24 @@ package com.palantir.javaformat.gradle; import com.palantir.javaformat.bootstrap.NativeImageFormatterService; -import com.palantir.javaformat.java.FormatterService; import java.io.File; import java.io.IOException; +import javax.inject.Inject; import org.gradle.api.DefaultTask; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.file.FileCollection; +import org.gradle.api.file.ProjectLayout; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; +import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; +import org.gradle.api.services.ServiceReference; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.TaskAction; -public final class PalantirJavaFormatPlugin implements Plugin { +public abstract class PalantirJavaFormatPlugin implements Plugin { @Override public void apply(Project project) { @@ -47,9 +50,17 @@ public void apply(Project project) { // spotless to try out our formatter project.getTasks().register("formatDiff", FormatDiffTask.class, task -> { if (NativeImageFormatProviderPlugin.isNativeImageConfigured(project)) { + System.err.println("Configuring native"); task.getNativeImage().fileProvider(getNativeImplConfiguration(project)); } }); + + project.getGradle() + .getSharedServices() + .registerIfAbsent("formatterService", FormatterServiceService.class, spec -> { + spec.getParameters().getExtension().set(project.provider(() -> project.getExtensions() + .getByType(JavaFormatExtension.class))); + }); }); } @@ -62,12 +73,18 @@ private static Provider getNativeImplConfiguration(Project project) { public abstract static class FormatDiffTask extends DefaultTask { + @Inject + protected abstract ProjectLayout getProjectLayout(); + private static Logger log = Logging.getLogger(FormatDiffTask.class); @org.gradle.api.tasks.Optional @InputFile abstract RegularFileProperty getNativeImage(); + @ServiceReference("formatterService") + abstract Property getFormatterServiceService(); + public FormatDiffTask() { setDescription("Format only chunks of files that appear in git diff"); setGroup("Formatting"); @@ -78,15 +95,14 @@ public final void formatDiff() throws IOException, InterruptedException { if (getNativeImage().isPresent()) { log.info("Using the native-image formatter"); FormatDiff.formatDiff( - getProject().getProjectDir().toPath(), + getProjectLayout().getProjectDirectory().getAsFile().toPath(), new NativeImageFormatterService( getNativeImage().get().getAsFile().toPath())); } else { log.info("Using the Java-based formatter"); - JavaFormatExtension extension = - getProject().getRootProject().getExtensions().getByType(JavaFormatExtension.class); - FormatterService formatterService = extension.serviceLoad(); - FormatDiff.formatDiff(getProject().getProjectDir().toPath(), formatterService); + FormatDiff.formatDiff( + getProjectLayout().getProjectDirectory().getAsFile().toPath(), + getFormatterServiceService().get().getFormatterService()); } } } diff --git a/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/FormatterServiceService.java b/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/FormatterServiceService.java new file mode 100644 index 000000000..21e7eea97 --- /dev/null +++ b/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/FormatterServiceService.java @@ -0,0 +1,40 @@ +/* + * (c) Copyright 2025 Palantir Technologies Inc. All rights reserved. + * + * 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 + * + * 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 com.palantir.javaformat.gradle; + +import com.palantir.javaformat.gradle.FormatterServiceService.Params; +import com.palantir.javaformat.java.FormatterService; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.services.BuildService; +import org.gradle.api.services.BuildServiceParameters; + +public abstract class FormatterServiceService implements BuildService { + public interface Params extends BuildServiceParameters { + Property getExtension(); + } + + Provider formatter; + + public FormatterServiceService() { + this.formatter = getParameters().getExtension().map(ext -> ext.serviceLoad()); + } + + public FormatterService getFormatterService() { + return this.formatter.get(); + } +} diff --git a/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/NativeImageFormatProviderPlugin.java b/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/NativeImageFormatProviderPlugin.java index 0b4ae8855..5409549c3 100644 --- a/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/NativeImageFormatProviderPlugin.java +++ b/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/NativeImageFormatProviderPlugin.java @@ -42,7 +42,11 @@ public void apply(Project rootProject) { return; } String implementationVersion = JavaFormatExtension.class.getPackage().getImplementationVersion(); - OperatingSystem operatingSystem = OperatingSystem.get(); + OperatingSystem operatingSystem = rootProject + .getProviders() + .of(OperatingSystemValueSource.class, spec -> {}) + .get(); + rootProject.getConfigurations().register(NATIVE_CONFIGURATION_NAME, conf -> { conf.setDescription("Internal configuration for resolving the palantir-java-format native image"); conf.setVisible(false); @@ -69,11 +73,13 @@ public void apply(Project rootProject) { } public static boolean isNativeImageConfigured(Project project) { - return isNativeImageSupported() && isNativeFlagEnabled(project); + return isNativeImageSupported(project) && isNativeFlagEnabled(project); } - private static boolean isNativeImageSupported() { - OperatingSystem os = OperatingSystem.get(); + private static boolean isNativeImageSupported(Project project) { + OperatingSystem os = project.getProviders() + .of(OperatingSystemValueSource.class, spec -> {}) + .get(); return os.equals(OperatingSystem.LINUX_GLIBC) || (os.equals(OperatingSystem.MACOS) && Architecture.get().equals(Architecture.AARCH64)); } diff --git a/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/OperatingSystemValueSource.java b/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/OperatingSystemValueSource.java new file mode 100644 index 000000000..b94af3663 --- /dev/null +++ b/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/OperatingSystemValueSource.java @@ -0,0 +1,32 @@ +/* + * (c) Copyright 2025 Palantir Technologies Inc. All rights reserved. + * + * 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 + * + * 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 com.palantir.javaformat.gradle; + +import com.palantir.platform.OperatingSystem; +import javax.inject.Inject; +import org.gradle.api.provider.ValueSource; +import org.gradle.api.provider.ValueSourceParameters; + +public abstract class OperatingSystemValueSource implements ValueSource { + + @Inject + public OperatingSystemValueSource() {} + + @Override + public OperatingSystem obtain() { + return OperatingSystem.get(); + } +} diff --git a/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/NativePalantirJavaFormatStep.java b/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/NativePalantirJavaFormatStep.java index cc92dd1d3..3ae6cf3d0 100644 --- a/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/NativePalantirJavaFormatStep.java +++ b/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/NativePalantirJavaFormatStep.java @@ -25,12 +25,13 @@ import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.function.Supplier; import org.gradle.api.artifacts.Configuration; import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; public final class NativePalantirJavaFormatStep { - private static Logger logger = Logging.getLogger(NativePalantirJavaFormatStep.class); + private static final Logger logger = Logging.getLogger(NativePalantirJavaFormatStep.class); private NativePalantirJavaFormatStep() {} @@ -38,28 +39,23 @@ private NativePalantirJavaFormatStep() {} /** Creates a step which formats everything - code, import order, and unused imports. */ public static FormatterStep create(Configuration configuration) { - return FormatterStep.createLazy( - NAME, - () -> { - File execFile = configuration.getSingleFile(); - logger.info("Using native-image at {}", configuration.getSingleFile()); - return new State(FileSignature.signAsSet(execFile)); - }, - State::createFormat); + return FormatterStep.createLazy(NAME, () -> new State(configuration::getSingleFile), State::createFormat); } static class State implements Serializable { private static final long serialVersionUID = 1L; - final FileSignature pathToExe; + private final transient Supplier execSupplier; - State(FileSignature pathToExe) { - this.pathToExe = pathToExe; + State(Supplier supplier) { + this.execSupplier = supplier; } String format(ProcessRunner runner, String input) throws IOException, InterruptedException { - List argumentsWithPathToExe = - List.of(pathToExe.getOnlyFile().getAbsolutePath(), "--palantir", "-"); + File execFile = execSupplier.get(); + logger.info("Using native-image at {}", execFile); + File signedFile = FileSignature.signAsSet(execFile).getOnlyFile(); + List argumentsWithPathToExe = List.of(signedFile.getAbsolutePath(), "--palantir", "-"); return runner.exec(input.getBytes(StandardCharsets.UTF_8), argumentsWithPathToExe) .assertExitZero(StandardCharsets.UTF_8); } diff --git a/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/PalantirJavaFormatStep.java b/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/PalantirJavaFormatStep.java index c18b15d0e..88e69a6e1 100644 --- a/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/PalantirJavaFormatStep.java +++ b/gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/PalantirJavaFormatStep.java @@ -39,7 +39,7 @@ public static FormatterStep create(Configuration palantirJavaFormat, JavaFormatE ensureImplementationNotDirectlyLoadable(); Supplier memoizedService = extension::serviceLoad; return FormatterStep.createLazy( - NAME, () -> new State(palantirJavaFormat.getFiles(), memoizedService), State::createFormat); + NAME, () -> new State(palantirJavaFormat::getFiles, memoizedService), State::createFormat); } static final class State implements Serializable { @@ -51,7 +51,9 @@ static final class State implements Serializable { // Kept for state serialization purposes. @SuppressWarnings({"unused", "FieldCanBeLocal"}) - private final FileSignature jarsSignature; + private FileSignature jarsSignature; + + private final transient Supplier> jarsSupplier; // Transient as this is not serializable. private final transient Supplier memoizedFormatter; @@ -59,17 +61,26 @@ static final class State implements Serializable { /** * Build a cacheable state for spotless from the given jars, that uses the given {@link FormatterService}. * - * @param jars The jars that contain the palantir-java-format implementation. This is only used for caching and + * @param jarsSupplier Supplies the jars that contain the palantir-java-format implementation. This is only used for caching and * up-to-dateness purposes. */ - State(Iterable jars, Supplier memoizedFormatter) throws IOException { - this.jarsSignature = FileSignature.signAsSet(jars); + State(Supplier> jarsSupplier, Supplier memoizedFormatter) { + this.jarsSupplier = jarsSupplier; this.memoizedFormatter = memoizedFormatter; } @SuppressWarnings("NullableProblems") FormatterFunc createFormat() { - return memoizedFormatter.get()::formatSourceReflowStringsAndFixImports; + return input -> { + try { + // Only resolve the jars and compute the signature at execution time! + Iterable jars = jarsSupplier.get(); + this.jarsSignature = FileSignature.signAsSet(jars); + return memoizedFormatter.get().formatSourceReflowStringsAndFixImports(input); + } catch (IOException e) { + throw new RuntimeException(e); + } + }; } } diff --git a/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatIdeaPluginTest.groovy b/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatIdeaPluginTest.groovy index 7d97c3ae7..bf7a6b14c 100644 --- a/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatIdeaPluginTest.groovy +++ b/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatIdeaPluginTest.groovy @@ -23,6 +23,11 @@ class PalantirJavaFormatIdeaPluginTest extends IntegrationTestKitSpec { private static final NATIVE_IMAGE_FILE = new File("build/nativeImage.path") private static final NATIVE_CONFIG = String.format("palantirJavaFormatNative files(\"%s\")", NATIVE_IMAGE_FILE.text) + def setup() { + definePluginOutsideOfPluginBlock = true + keepFiles = true + } + def "idea_configuresIpr"() { file('gradle.properties') << extraGradleProperties @@ -39,7 +44,7 @@ class PalantirJavaFormatIdeaPluginTest extends IntegrationTestKitSpec { """.replace("EXTRA_CONFIGURATION", extraDependencies).stripIndent() when: - runTasks('idea') + runTasks('idea --configuration-cache') then: def iprFile = new File(projectDir, "${moduleName}.ipr") diff --git a/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatPluginTest.groovy b/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatPluginTest.groovy index 26e97731d..6078cee40 100644 --- a/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatPluginTest.groovy +++ b/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatPluginTest.groovy @@ -26,6 +26,11 @@ class PalantirJavaFormatPluginTest extends IntegrationTestKitSpec { private static final NATIVE_IMAGE_FILE = new File("build/nativeImage.path").absolutePath private static final NATIVE_CONFIG = "palantirJavaFormatNative files(file(\"${NATIVE_IMAGE_FILE}\").text)" + def setup() { + definePluginOutsideOfPluginBlock = true + keepFiles = true + } + @Unroll def 'formatDiff updates only lines changed in git diff'(String extraGradleProperties, String expectedOutput) { file('gradle.properties') << extraGradleProperties @@ -78,7 +83,7 @@ class PalantirJavaFormatPluginTest extends IntegrationTestKitSpec { '''.stripIndent() when: - def result = runTasks('formatDiff', '--info') + def result = runTasks('formatDiff', '--info', '--configuration-cache') then: result.output.contains(expectedOutput) diff --git a/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatSpotlessPluginTest.groovy b/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatSpotlessPluginTest.groovy index 90a56547d..609ac6ffc 100644 --- a/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatSpotlessPluginTest.groovy +++ b/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatSpotlessPluginTest.groovy @@ -57,6 +57,10 @@ class PalantirJavaFormatSpotlessPluginTest extends IntegrationTestKitSpec { 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' + + constraints { + classpath 'com.diffplug.spotless:6.22.0' + } } } @@ -105,7 +109,7 @@ class PalantirJavaFormatSpotlessPluginTest extends IntegrationTestKitSpec { when: - def result = runGradlewTasks('spotlessApply', '--info') + def result = runGradlewTasks('spotlessApply', '--info', '--configuration-cache') then: result.standardOutput.contains(expectedOutput)