Skip to content

Commit 7a8f7cf

Browse files
committed
feat: poc for invoking GJF in native cli
(still very hacky but working)
1 parent 3b33804 commit 7a8f7cf

File tree

14 files changed

+280
-18
lines changed

14 files changed

+280
-18
lines changed

cli/build.gradle

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ dependencies {
3636
annotationProcessor "info.picocli:picocli-codegen:${VER_PICOCLI}"
3737
}
3838

39+
dependencies {
40+
[
41+
'com.google.googlejavaformat:google-java-format:1.24.0'
42+
].each {
43+
implementation it
44+
}
45+
}
46+
3947
apply from: rootProject.file('gradle/special-tests.gradle')
4048
tasks.withType(Test).configureEach {
4149
testLogging.showStandardStreams = true
@@ -88,15 +96,45 @@ application {
8896

8997
// use tasks 'nativeCompile' and 'nativeRun' to compile and run the native image
9098
graalvmNative {
99+
agent {
100+
enabled = true
101+
defaultMode = "standard"
102+
metadataCopy {
103+
inputTaskNames.add('test')
104+
mergeWithExisting = true
105+
}
106+
}
91107
binaries {
92108
main {
93109
imageName = 'spotless'
94110
mainClass = 'com.diffplug.spotless.cli.SpotlessCLI'
95111
sharedLibrary = false
112+
useFatJar = true // use shadowJar as input to have same classpath
113+
114+
// optimizations, see https://www.graalvm.org/latest/reference-manual/native-image/optimizations-and-performance/
115+
//buildArgs.add('-O3') // on production builds
116+
117+
// the following options are required for GJF
118+
// see: <https://github.com/google/google-java-format/issues/894#issuecomment-1430408909>
119+
buildArgs.add('-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED')
120+
buildArgs.add('-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED')
121+
buildArgs.add('-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED')
122+
buildArgs.add('-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED')
123+
buildArgs.add('-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED')
124+
125+
buildArgs.add('--initialize-at-build-time=com.sun.tools.javac.file.Locations')
126+
127+
buildArgs.add('-H:IncludeResourceBundles=com.sun.tools.javac.resources.compiler')
128+
buildArgs.add('-H:IncludeResourceBundles=com.sun.tools.javac.resources.javac')
96129
}
97130
}
98131
}
99132

133+
tasks.named('nativeCompile') {
134+
dependsOn('shadowJar')
135+
classpathJar = tasks.shadowJar.archiveFile.get().asFile
136+
}
137+
100138
tasks.withType(Test).configureEach {
101139
if (it.name == 'testCliProcess') {
102140
it.dependsOn('shadowJar')

cli/src/main/java/com/diffplug/spotless/cli/SpotlessCLI.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,18 @@
3232
import com.diffplug.spotless.cli.core.TargetResolver;
3333
import com.diffplug.spotless.cli.execution.SpotlessExecutionStrategy;
3434
import com.diffplug.spotless.cli.help.OptionConstants;
35-
import com.diffplug.spotless.cli.steps.generic.LicenseHeader;
36-
import com.diffplug.spotless.cli.steps.generic.RemoveMeLaterSubCommand;
35+
import com.diffplug.spotless.cli.steps.GoogleJavaFormat;
36+
import com.diffplug.spotless.cli.steps.LicenseHeader;
37+
import com.diffplug.spotless.cli.steps.RemoveMeLater;
3738
import com.diffplug.spotless.cli.version.SpotlessCLIVersionProvider;
3839

3940
import picocli.CommandLine;
4041
import picocli.CommandLine.Command;
4142

4243
@Command(name = "spotless", mixinStandardHelpOptions = true, versionProvider = SpotlessCLIVersionProvider.class, description = "Runs spotless", subcommandsRepeatable = true, subcommands = {
4344
LicenseHeader.class,
44-
RemoveMeLaterSubCommand.class})
45+
RemoveMeLater.class,
46+
GoogleJavaFormat.class})
4547
public class SpotlessCLI implements SpotlessAction, SpotlessCommand, SpotlessActionContextProvider {
4648

4749
@CommandLine.Option(names = {"-V", "--version"}, versionHelp = true, description = "Print version information and exit.")
@@ -76,7 +78,8 @@ public Integer executeSpotlessAction(@Nonnull List<FormatterStep> formatterSteps
7678
.build()) {
7779

7880
ResultType resultType = targetResolver.resolveTargets()
79-
.parallel() // needed?
81+
.parallel()
82+
.peek(path -> System.out.printf("%s: formatting %s%n", Thread.currentThread().getName(), path))
8083
.map(path -> ThrowingEx.get(() -> new Result(path, LintState.of(formatter, path.toFile())))) // TODO handle suppressions, see SpotlessTaskImpl
8184
.map(result -> this.handleResult(formatter, result))
8285
.reduce(ResultType.CLEAN, ResultType::combineWith);
@@ -116,7 +119,8 @@ public static void main(String... args) {
116119
// args = new String[]{"--version"};
117120
// args = new String[]{"license-header", "--header-file", "CHANGES.md", "--delimiter-for", "java", "license-header", "--header", "abc"};
118121

119-
args = new String[]{"--mode=CHECK", "--target", "src/poc/java/**/*.java", "--encoding=UTF-8", "license-header", "--header", "abc", "license-header", "--header-file", "TestHeader.txt"};
122+
// args = new String[]{"--mode=CHECK", "--target", "src/poc/java/**/*.java", "--encoding=UTF-8", "license-header", "--header", "abc", "license-header", "--header-file", "TestHeader.txt"};
123+
args = new String[]{"--basedir", "cli", "--target", "src/poc/java/**/*.java", "--encoding=UTF-8", "google-java-format"};
120124
// args = new String[]{"--version"};
121125
}
122126
int exitCode = createCommandLine(createInstance())
@@ -166,6 +170,7 @@ ResultType handleResult(Formatter formatter, Result result) {
166170
if (result.lintState.isHasLints()) {
167171
// something went wrong, we should not apply the changes
168172
System.err.println("File has lints: " + result.target.toFile().getName());
173+
System.err.println("lint:\n" + result.lintState.asStringDetailed(result.target.toFile(), formatter));
169174
return ResultType.DIRTY;
170175
}
171176
ThrowingEx.run(() -> result.lintState.getDirtyState().writeCanonicalTo(result.target.toFile()));
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2024 DiffPlug
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+
* http://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+
package com.diffplug.spotless.cli.core;
17+
18+
import java.io.File;
19+
import java.nio.charset.StandardCharsets;
20+
import java.nio.file.Files;
21+
import java.util.Collection;
22+
import java.util.List;
23+
import java.util.Set;
24+
25+
import com.diffplug.spotless.JarState;
26+
import com.diffplug.spotless.Provisioner;
27+
28+
public class CliJarProvisioner implements Provisioner {
29+
30+
public static final CliJarProvisioner INSTANCE = new CliJarProvisioner();
31+
32+
public static final File OWN_JAR = createSentinelFile();
33+
34+
public CliJarProvisioner() {
35+
JarState.setOverrideClassLoader(getClass().getClassLoader()); // use the classloader of this class
36+
// TODO (simschla, 11.11.2024): THIS IS A HACK, replace with proper solution
37+
}
38+
39+
private static File createSentinelFile() {
40+
try {
41+
File file = File.createTempFile("spotless-cli", ".jar");
42+
Files.write(file.toPath(), List.of("@@@@PLACEHOLDER_FOR_OWN_JAR@@@@"), StandardCharsets.UTF_8, java.nio.file.StandardOpenOption.TRUNCATE_EXISTING);
43+
file.deleteOnExit();
44+
return file;
45+
} catch (Exception e) {
46+
throw new RuntimeException("Could not create sentinel file", e);
47+
}
48+
}
49+
50+
@Override
51+
public Set<File> provisionWithTransitives(boolean withTransitives, Collection<String> mavenCoordinates) {
52+
return Set.of(OWN_JAR);
53+
}
54+
55+
}

cli/src/main/java/com/diffplug/spotless/cli/core/SpotlessActionContext.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
import javax.annotation.Nonnull;
2323

24+
import com.diffplug.spotless.Provisioner;
25+
2426
public class SpotlessActionContext {
2527

2628
private final TargetFileTypeInferer.TargetFileType targetFileType;
@@ -43,4 +45,8 @@ public File resolveFile(File file) {
4345
public Path resolvePath(Path path) {
4446
return fileResolver.resolvePath(path);
4547
}
48+
49+
public Provisioner provisioner() {
50+
return CliJarProvisioner.INSTANCE;
51+
}
4652
}

cli/src/main/java/com/diffplug/spotless/cli/core/TargetResolver.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,16 @@ public TargetResolver(@Nonnull Path baseDir, @Nonnull List<String> targets) {
4949
}
5050

5151
public Stream<Path> resolveTargets() {
52-
return targets.stream()
53-
.flatMap(this::resolveTarget);
52+
return targets.parallelStream()
53+
.map(this::resolveTarget)
54+
.reduce(Stream::concat) // beware! when using flatmap, the stream goes to sequential
55+
.orElse(Stream.empty());
5456
}
5557

5658
private Stream<Path> resolveTarget(String target) {
5759

5860
final boolean isGlob = target.contains("*") || target.contains("?");
61+
System.out.println("isGlob: " + isGlob + " target: " + target);
5962

6063
if (isGlob) {
6164
return resolveGlob(target);
@@ -83,7 +86,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
8386
return FileVisitResult.CONTINUE;
8487
}
8588
}));
86-
return collected.stream();
89+
return collected.parallelStream();
8790
}
8891

8992
private Stream<Path> resolveGlob(String glob) {
@@ -108,13 +111,15 @@ private Stream<Path> resolveGlob(String glob) {
108111
new SimpleFileVisitor<>() {
109112
@Override
110113
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
111-
if (matcher.matches(file)) {
114+
Path relativeFile = startDir.relativize(file);
115+
if (matcher.matches(relativeFile)) {
116+
System.out.println("Matched: " + file);
112117
collected.add(file);
113118
}
114119
return FileVisitResult.CONTINUE;
115120
}
116121
}));
117-
return collected.stream()
122+
return collected.parallelStream()
118123
.map(Path::normalize);
119124
// .map(Path::toAbsolutePath);
120125
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2024 DiffPlug
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+
* http://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+
package com.diffplug.spotless.cli.steps;
17+
18+
import java.util.List;
19+
20+
import com.diffplug.spotless.FormatterStep;
21+
import com.diffplug.spotless.cli.core.SpotlessActionContext;
22+
import com.diffplug.spotless.cli.help.OptionConstants;
23+
import com.diffplug.spotless.java.GoogleJavaFormatStep;
24+
25+
import picocli.CommandLine;
26+
27+
@CommandLine.Command(name = "google-java-format", description = "Runs google java format")
28+
public class GoogleJavaFormat extends SpotlessFormatterStep {
29+
30+
@CommandLine.Option(names = {"--style", "-s"}, required = false, defaultValue = "GOOGLE", description = "The style to use for the google java format." + OptionConstants.VALID_VALUES_SUFFIX + OptionConstants.DEFAULT_VALUE_SUFFIX)
31+
Style style;
32+
33+
@CommandLine.Option(names = {"--reflow-long-strings", "-r"}, required = false, defaultValue = "false", description = "Reflow long strings." + OptionConstants.DEFAULT_VALUE_SUFFIX)
34+
boolean reflowLongStrings;
35+
36+
@CommandLine.Option(names = {"--reorder-imports", "-i"}, required = false, defaultValue = "false", description = "Reorder imports." + OptionConstants.DEFAULT_VALUE_SUFFIX)
37+
boolean reorderImports;
38+
39+
@CommandLine.Option(names = {"--format-javadoc", "-j"}, required = false, defaultValue = "true", description = "Format javadoc." + OptionConstants.DEFAULT_VALUE_SUFFIX)
40+
boolean formatJavadoc;
41+
42+
@Override
43+
public List<FormatterStep> prepareFormatterSteps(SpotlessActionContext context) {
44+
return List.of(GoogleJavaFormatStep.create(
45+
GoogleJavaFormatStep.defaultGroupArtifact(),
46+
GoogleJavaFormatStep.defaultVersion(),
47+
style.name(),
48+
context.provisioner(),
49+
reflowLongStrings,
50+
reorderImports,
51+
formatJavadoc));
52+
}
53+
54+
public enum Style {
55+
AOSP, GOOGLE
56+
}
57+
}

cli/src/main/java/com/diffplug/spotless/cli/steps/generic/LicenseHeader.java renamed to cli/src/main/java/com/diffplug/spotless/cli/steps/LicenseHeader.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.diffplug.spotless.cli.steps.generic;
16+
package com.diffplug.spotless.cli.steps;
1717

1818
import java.io.File;
1919
import java.nio.file.Files;
@@ -34,7 +34,7 @@
3434
import picocli.CommandLine;
3535

3636
@CommandLine.Command(name = "license-header", description = "Runs license header")
37-
public class LicenseHeader extends SpotlessFormatterStepSubCommand {
37+
public class LicenseHeader extends SpotlessFormatterStep {
3838

3939
@CommandLine.ArgGroup(exclusive = true, multiplicity = "1")
4040
LicenseHeaderSourceOption licenseHeaderSourceOption;
@@ -49,6 +49,8 @@ static class LicenseHeaderSourceOption {
4949
File headerFile;
5050
}
5151

52+
// TODO add more config options
53+
5254
@Nonnull
5355
@Override
5456
public List<FormatterStep> prepareFormatterSteps(SpotlessActionContext context) {
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.diffplug.spotless.cli.steps.generic;
16+
package com.diffplug.spotless.cli.steps;
1717

1818
import java.io.File;
1919
import java.util.List;
@@ -25,7 +25,7 @@
2525
import picocli.CommandLine;
2626

2727
@CommandLine.Command(name = "ignoreme")
28-
public class RemoveMeLaterSubCommand extends SpotlessFormatterStepSubCommand {
28+
public class RemoveMeLater extends SpotlessFormatterStep {
2929

3030
@CommandLine.ArgGroup(exclusive = true, multiplicity = "1")
3131
LicenseHeaderOption licenseHeaderOption;
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,19 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.diffplug.spotless.cli.steps.generic;
16+
package com.diffplug.spotless.cli.steps;
1717

1818
import java.util.List;
1919

2020
import javax.annotation.Nonnull;
2121

2222
import com.diffplug.spotless.FormatterStep;
2323
import com.diffplug.spotless.cli.core.SpotlessActionContext;
24-
import com.diffplug.spotless.cli.steps.SpotlessCLIFormatterStep;
2524

2625
import picocli.CommandLine;
2726

2827
@CommandLine.Command(mixinStandardHelpOptions = true)
29-
public abstract class SpotlessFormatterStepSubCommand implements SpotlessCLIFormatterStep {
28+
public abstract class SpotlessFormatterStep implements SpotlessCLIFormatterStep {
3029

3130
@Nonnull
3231
@Override
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2024 DiffPlug
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+
* http://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+
package com.diffplug.spotless.cli.steps;
17+
18+
import com.diffplug.spotless.tag.CliProcessTest;
19+
20+
@CliProcessTest
21+
public class GoogleJavaFormatJavaProcessTest extends GoogleJavaFormatTest {}

0 commit comments

Comments
 (0)