Skip to content

Commit a89167b

Browse files
authored
Merge branch '21.X.X' into restore-style-palantir-alias
2 parents e27531c + c7354ce commit a89167b

96 files changed

Lines changed: 2659 additions & 752 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CLAUDE.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ Building and testing: the README's "Building and testing locally" section. Gradl
88
- **Collections.** Plain `java.util` types. `List.of`, `List.copyOf`, `Map.of` and `Stream.toList()` give
99
unmodifiable collections, so new code does not need Guava's `ImmutableList` or `ImmutableMap`; use them only
1010
where an existing API already hands one over.
11-
- **Nullability.** One annotation, `javax.annotation.Nullable` (jsr305), the one the code uses; do not add another
12-
`Nullable`.
11+
- **Nullability.** One annotation, JSpecify's `org.jspecify.annotations.Nullable`; do not add another `Nullable`,
12+
nor a `NotNull` or `NonNull`: what is not `@Nullable` is non-null, and NullAway checks it. It is a type-use
13+
annotation, so on a nested type it goes before the simple name (`Input.@Nullable Token`), and on an array before
14+
the brackets (`String @Nullable []`).
1315
- **Local variables.** `var` wherever the initializer gives the type unchanged, in `for` loops too. Not where it
1416
would box a primitive (`for (var n : List<Integer>)`), and not without an initializer that has a type of its own
1517
(a lambda, `null`).

‎buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle‎

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,36 @@ dependencies {
1111
}
1212

1313
nullaway {
14+
severity = net.ltgt.gradle.errorprone.CheckSeverity.ERROR
1415
annotatedPackages.add('com.palantir')
16+
jspecifyMode = true
1517
}
1618

1719
tasks.withType(JavaCompile).configureEach {
20+
// JSpecify mode reads @Nullable on types from class files, which javac 21 does only with this flag.
21+
options.compilerArgs.add('-XDaddTypeAnnotationsToSymbol=true')
1822
options.errorprone {
1923
// Annotation processor output (Immutables) is not ours to fix.
2024
excludedPaths = '.*/build/generated/.*'
25+
nullaway {
26+
handleTestAssertionLibraries = true
27+
}
28+
error('CanonicalAnnotationSyntax')
29+
error('CollectorMutability')
30+
error('DirectReturn')
31+
error('EffectivelyPrivate')
32+
error('FormatStringConcatenation')
33+
error('JUnitClassModifiers')
34+
error('JUnitMethodDeclaration')
35+
error('RedundantStringConversion')
36+
error('RefactorSwitch')
37+
error('SameNameButDifferent')
38+
error('StringConcatToTextBlock')
39+
error('UnusedVariable')
40+
disable('IsInstanceLambdaUsage')
41+
disable('LexicographicalAnnotationAttributeListing')
42+
disable('LexicographicalAnnotationListing')
43+
disable('StringJoining')
2144
}
2245
}
2346

@@ -27,27 +50,3 @@ pluginManager.withPlugin('java-gradle-plugin') {
2750
options.errorprone.disable('JavaxInjectOnAbstractMethod')
2851
}
2952
}
30-
31-
// The checks vfs-s3 makes build-breaking. The commented ones stay off until the code is fixed, one check at a
32-
// time: StaticImport 84, LexicographicalAnnotationListing 46, RedundantStringConversion 6, AlmostJavadoc 3
33-
// findings; the rest have none.
34-
tasks.withType(JavaCompile).configureEach {
35-
options.errorprone {
36-
//error('StaticImport')
37-
//error('UnnecessarilyFullyQualified')
38-
//error('EmptyBlockTag')
39-
//error('OperatorPrecedence')
40-
//error('RedundantStringConversion')
41-
//error('PatternMatchingInstanceof')
42-
//error('UnnecessaryParentheses')
43-
//error('LexicographicalAnnotationListing')
44-
error('JUnitMethodDeclaration')
45-
error('JUnitClassModifiers')
46-
//error('MissingSummary')
47-
//error('AlmostJavadoc')
48-
//error('BadImport')
49-
//error('JdkObsolete')
50-
// NonStaticImport (Picnic) is the inverse of StaticImport and would give contradictory advice.
51-
//disable('NonStaticImport')
52-
}
53-
}

‎gradle-open-java-format/build.gradle‎

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ gradlePlugin {
7070
openJavaFormatProvider {
7171
id = 'dev.openjavaformat.java-format-provider'
7272
implementationClass = 'com.palantir.javaformat.gradle.PalantirJavaFormatProviderPlugin'
73-
description = 'Exposes a configuration containing the open-java-format jars'
73+
description = 'Declares palantirJavaFormat, which overrides the formatter that comes inside the open-java-format plugins'
7474
displayName = 'Open Java Format Provider'
7575
tags.set(['java', 'style'])
7676
}
@@ -117,8 +117,9 @@ tasks.register("copyNativeImage", Copy.class) {
117117
into("$buildDir/nativeImage")
118118
}
119119

120-
// Split in two so the jar build does not need a GraalVM image: every test reads
121-
// impl.classpath, only the four that drive the binary read nativeImage.path.
120+
// Split in two so the jar build does not need a GraalVM image: the tests that declare the
121+
// formatter themselves read impl.classpath, which is also what bundleFormatter puts inside the
122+
// plugin; only the ones that drive the binary read nativeImage.path.
122123
//
123124
// Everything the doLast bodies touch is captured here, at configuration time: reaching for
124125
// `configurations` or `tasks` from inside them would pull the whole script into the task and
@@ -136,6 +137,31 @@ def writeImplClasspath = tasks.register("writeImplClasspath") {
136137
}
137138
}
138139

140+
// The Java-based formatter travels inside the plugin, so that a build needs no repository and no dependency for it,
141+
// and runs the formatter of the plugin's own commit (#20). The jars go in unchanged, each with its own LICENSE and
142+
// NOTICE, next to jars.txt: a line per jar, "<sha256> <name>", in the order of the classpath. BundledFormatter
143+
// extracts them on first use.
144+
def bundledFormatterDir = layout.buildDirectory.dir("generated/bundledFormatter")
145+
def bundledFormatterPath = "open-java-format/formatter"
146+
def bundleFormatter = tasks.register("bundleFormatter", Sync) {
147+
into(bundledFormatterDir)
148+
into(bundledFormatterPath) {
149+
from(implFiles)
150+
}
151+
doLast {
152+
def lines = implFiles.files.collect { jar ->
153+
"${java.security.MessageDigest.getInstance('SHA-256').digest(jar.bytes).encodeHex()} ${jar.name}"
154+
}
155+
bundledFormatterDir.get().file("${bundledFormatterPath}/jars.txt").asFile.text = lines.join('\n') + '\n'
156+
}
157+
}
158+
sourceSets.main.resources.srcDir(bundleFormatter)
159+
160+
// The sources jar takes the resources too; the formatter's jars are not the plugin's sources.
161+
tasks.named('sourcesJar', Jar) {
162+
exclude("${bundledFormatterPath}/**")
163+
}
164+
139165
def writeNativeImagePath = tasks.register("writeNativeImagePath") {
140166
inputs.files(tasks.named("copyNativeImage"))
141167
outputs.file(nativeImagePathFile)

‎gradle-open-java-format/src/main/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatIdeaPlugin.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public void apply(Project rootProject) {
4646
.from(rootProject
4747
.getConfigurations()
4848
.getByName(PalantirJavaFormatProviderPlugin.CONFIGURATION_NAME));
49+
task.getBundledFormatter().set(BundledFormatter.hash());
50+
task.getGradleUserHome().set(rootProject.getGradle().getGradleUserHomeDir());
4951
});
5052

5153
TaskProvider<UpdateWorkspaceXmlFile> updateWorkspaceXml = rootProject

‎gradle-open-java-format/src/main/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatPlugin.java‎

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,16 @@
1717
package com.palantir.javaformat.gradle;
1818

1919
import com.palantir.javaformat.bootstrap.NativeImageFormatterService;
20-
import java.io.File;
2120
import java.io.IOException;
2221
import org.gradle.api.DefaultTask;
2322
import org.gradle.api.Plugin;
2423
import org.gradle.api.Project;
25-
import org.gradle.api.artifacts.Configuration;
2624
import org.gradle.api.file.ConfigurableFileCollection;
2725
import org.gradle.api.file.DirectoryProperty;
2826
import org.gradle.api.file.FileCollection;
2927
import org.gradle.api.file.RegularFileProperty;
3028
import org.gradle.api.logging.Logger;
3129
import org.gradle.api.logging.Logging;
32-
import org.gradle.api.provider.Provider;
3330
import org.gradle.api.tasks.Classpath;
3431
import org.gradle.api.tasks.InputFile;
3532
import org.gradle.api.tasks.Internal;
@@ -53,31 +50,23 @@ public void apply(Project project) {
5350
project.getPlugins().apply(PalantirJavaFormatSpotlessPlugin.class);
5451

5552
project.getPlugins().withId("java", p -> {
53+
var formatterClasspath = FormatterClasspath.of(project);
54+
var nativeImage = FormatterClasspath.nativeImageOf(project);
5655

5756
// TODO(dfox): in the future we may want to offer a simple 'format' task so people don't need to use
5857
// spotless to try out our formatter
5958
project.getTasks().register("formatDiff", FormatDiffTask.class, task -> {
6059
task.getProjectDirectory().set(project.getLayout().getProjectDirectory());
60+
task.getGradleUserHome().set(project.getGradle().getGradleUserHomeDir());
6161
if (getNativeImageSupport().isNativeImageConfigured()) {
62-
task.getNativeImage().fileProvider(getNativeImplConfiguration(project));
62+
task.getNativeImage().fileProvider(nativeImage.map(FileCollection::getSingleFile));
6363
} else {
64-
task.getFormatterClasspath().from(getImplConfiguration(project));
64+
task.getFormatterClasspath().from(formatterClasspath);
6565
}
6666
});
6767
});
6868
}
6969

70-
private static Provider<Configuration> getImplConfiguration(Project project) {
71-
return project.getRootProject().getConfigurations().named(PalantirJavaFormatProviderPlugin.CONFIGURATION_NAME);
72-
}
73-
74-
private static Provider<File> getNativeImplConfiguration(Project project) {
75-
return (project.getRootProject()
76-
.getConfigurations()
77-
.named(NativeImageFormatProviderPlugin.NATIVE_CONFIGURATION_NAME)
78-
.map(FileCollection::getSingleFile));
79-
}
80-
8170
@DisableCachingByDefault(
8271
because = "Reformats whatever the working tree currently differs by; there is no reusable output")
8372
public abstract static class FormatDiffTask extends DefaultTask {
@@ -89,10 +78,17 @@ public abstract static class FormatDiffTask extends DefaultTask {
8978
@PathSensitive(PathSensitivity.NAME_ONLY)
9079
abstract RegularFileProperty getNativeImage();
9180

92-
/** The jars of the Java-based formatter, which runs when the native image is not configured. */
81+
/**
82+
* The jars a build declared for the Java-based formatter, which runs when the native image is not configured.
83+
* Most builds declare none, and the task then runs the one inside the plugin.
84+
*/
9385
@Classpath
9486
abstract ConfigurableFileCollection getFormatterClasspath();
9587

88+
/** Where the formatter inside the plugin is extracted to, and the native image copied to. */
89+
@Internal
90+
abstract DirectoryProperty getGradleUserHome();
91+
9692
/**
9793
* The project whose changed lines are formatted. The task reads it here rather than from {@code getProject()},
9894
* which a task must not call when it runs with the configuration cache.
@@ -112,13 +108,16 @@ public final void formatDiff() throws IOException, InterruptedException {
112108
log.info("Using the native-image formatter");
113109
FormatDiff.formatDiff(
114110
projectDir,
115-
new NativeImageFormatterService(
116-
getNativeImage().get().getAsFile().toPath()));
111+
new NativeImageFormatterService(NativeImageAtomicCopy.copyToCache(
112+
getNativeImage().get().getAsFile().toPath(),
113+
getGradleUserHome().get().getAsFile())));
117114
} else {
118115
log.info("Using the Java-based formatter");
119116
FormatDiff.formatDiff(
120117
projectDir,
121-
FormatterServiceLoader.load(getFormatterClasspath().getFiles()));
118+
FormatterServiceLoader.load(BundledFormatter.jarsUnlessOverridden(
119+
getFormatterClasspath().getFiles(),
120+
getGradleUserHome().get().getAsFile())));
122121
}
123122
}
124123
}

‎gradle-open-java-format/src/main/groovy/com/palantir/javaformat/gradle/UpdatePalantirJavaFormatIdeaXmlFile.java‎

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,69 @@
2020
import java.net.URI;
2121
import java.util.List;
2222
import org.gradle.api.DefaultTask;
23+
import org.gradle.api.Task;
2324
import org.gradle.api.file.ConfigurableFileCollection;
25+
import org.gradle.api.file.DirectoryProperty;
2426
import org.gradle.api.file.RegularFileProperty;
27+
import org.gradle.api.provider.Property;
28+
import org.gradle.api.specs.Spec;
2529
import org.gradle.api.tasks.Classpath;
30+
import org.gradle.api.tasks.Input;
2631
import org.gradle.api.tasks.InputFiles;
32+
import org.gradle.api.tasks.Internal;
2733
import org.gradle.api.tasks.OutputFile;
2834
import org.gradle.api.tasks.TaskAction;
2935
import org.gradle.work.DisableCachingByDefault;
3036

3137
@DisableCachingByDefault(because = "Writes the developer's local .idea configuration; nothing to cache")
3238
public abstract class UpdatePalantirJavaFormatIdeaXmlFile extends DefaultTask {
3339

40+
/**
41+
* The jars a build declared for the formatter. Most builds declare none, and the IDE then gets the formatter inside
42+
* the plugin.
43+
*/
3444
@InputFiles
3545
@Classpath
3646
public abstract ConfigurableFileCollection getImplementationConfig();
3747

48+
/** The hash of the formatter inside the plugin, which names the directory its jars are extracted into. */
49+
@Input
50+
public abstract Property<String> getBundledFormatter();
51+
52+
/** Where the formatter inside the plugin is extracted to. */
53+
@Internal
54+
public abstract DirectoryProperty getGradleUserHome();
55+
56+
public UpdatePalantirJavaFormatIdeaXmlFile() {
57+
// The IDE loads the jars from where the XML says: while they are gone, the task is not up to date.
58+
getOutputs().upToDateWhen(new BundledFormatterIsExtracted());
59+
}
60+
3861
@org.gradle.api.tasks.Optional
3962
@OutputFile
4063
public abstract RegularFileProperty getXmlOutputFile();
4164

4265
@TaskAction
4366
public final void updateXml() {
44-
List<URI> uris =
45-
getImplementationConfig().getFiles().stream().map(File::toURI).toList();
67+
List<URI> uris = BundledFormatter.jarsUnlessOverridden(
68+
getImplementationConfig().getFiles(),
69+
getGradleUserHome().get().getAsFile())
70+
.stream()
71+
.map(File::toURI)
72+
.toList();
4673
XmlUtils.updateIdeaXmlFile(
4774
getXmlOutputFile().getAsFile().get(),
4875
node -> ConfigureJavaFormatterXml.configureJavaFormat(node, uris));
4976
}
77+
78+
/** A class, not a lambda: the configuration cache stores it with the task. */
79+
private static final class BundledFormatterIsExtracted implements Spec<Task> {
80+
@Override
81+
public boolean isSatisfiedBy(Task task) {
82+
var xml = (UpdatePalantirJavaFormatIdeaXmlFile) task;
83+
return !xml.getImplementationConfig().isEmpty()
84+
|| BundledFormatter.isExtracted(
85+
xml.getGradleUserHome().get().getAsFile());
86+
}
87+
}
5088
}

0 commit comments

Comments
 (0)