Skip to content
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

Added plugin for formatting Java sources using Palantir #3531

Merged
merged 27 commits into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f8f6eb2
Added plugin for formatting Java sources using Palantir
ajaychandran Sep 12, 2024
42fbd05
Merge remote-tracking branch 'origin/main' into palantir
ajaychandran Sep 12, 2024
9ea2d1f
Added test cases
ajaychandran Sep 12, 2024
13809c8
Added plugin for formatting Java sources using Palantir
ajaychandran Sep 12, 2024
ed8fa89
Added test cases
ajaychandran Sep 12, 2024
a9436eb
Rollback accidental format
ajaychandran Sep 13, 2024
6c55876
Renamed module to palantirjavaformat
ajaychandran Sep 13, 2024
779b79a
Renamed plugin and support options with file
ajaychandran Sep 13, 2024
90d06c8
Cleanup
ajaychandran Sep 13, 2024
0cc6713
Defined settings for JVM args
ajaychandran Sep 17, 2024
44761ec
Added formatAll
ajaychandran Sep 18, 2024
f9c7970
Added/updated documentation.
ajaychandran Sep 18, 2024
ed1a04a
Improved options file resolution and tests.
ajaychandran Sep 18, 2024
f78d73c
Added example test
ajaychandran Sep 18, 2024
16b08ea
Merge branch 'main' into palantir
ajaychandran Sep 18, 2024
a35b15c
Resolved warnings due to deprecations
ajaychandran Sep 18, 2024
d483959
Merge remote-tracking branch 'origin/palantir' into palantir
ajaychandran Sep 18, 2024
0268fe0
Cleanup
ajaychandran Sep 18, 2024
afbf129
Undo format changes
ajaychandran Sep 18, 2024
4fcdaa7
Undo format changes
ajaychandran Sep 18, 2024
15ee813
Changed options URL
ajaychandran Sep 19, 2024
527f589
Merge remote-tracking branch 'origin/main' into palantir
ajaychandran Sep 21, 2024
e754cc4
Refactored module/task names and location.
ajaychandran Sep 21, 2024
50fe106
Fixed tests and examples
ajaychandran Sep 21, 2024
bdfea14
Added example for external module usage
ajaychandran Sep 21, 2024
2837ed2
Moved link to source file above usage bloack.
ajaychandran Sep 21, 2024
0fc8729
Use mainargs.Flag for check argument.
ajaychandran Sep 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions example/javalib/linting/3-palantirformat/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package build

import mill._
import mill.javalib.palantirformat._

object `package` extends RootModule with PalantirFormatModule {
}

/** See Also: src/A.java */

/** Usage

> ./mill palantirformat --check # check should fail initially
...checking format in java sources ...
...src/A.java
error: ...palantirformat aborted due to format error(s) (or invalid plugin settings/palantirformat options)

> ./mill palantirformat # format all Java source files
...formatting java sources ...

> ./mill palantirformat --check # check should succeed now
...checking format in java sources ...

> ./mill mill.javalib.palantirformat.PalantirFormatModule/ __.sources # alternatively, use external module to check/format
...formatting java sources ...
*/
ajaychandran marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 6 additions & 0 deletions example/javalib/linting/3-palantirformat/src/A.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
public class A {

public static void main(String[] args) {
System.out.println("hello"); // indentation should be fixed
}
}
190 changes: 190 additions & 0 deletions scalalib/src/mill/javalib/palantirformat/PalantirFormatModule.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package mill
package javalib.palantirformat

import mill.api.{Loose, PathRef}
import mill.define.{Discover, ExternalModule}
import mill.main.Tasks
import mill.scalalib.{CoursierModule, DepSyntax, JavaModule}
import mill.util.Jvm

trait PalantirFormatBaseModule extends CoursierModule {

/**
* Classpath for running Palantir Java Format.
*/
def palantirformatClasspath: T[Loose.Agg[PathRef]] = T {
defaultResolver().resolveDeps(
Agg(ivy"com.palantir.javaformat:palantir-java-format:${palantirformatVersion()}")
)
}

/**
* JVM arguments for running Palantir Java Format. Defaults to values prescribed in
* "[[https://github.com/palantir/palantir-java-format/issues/548 Broken on Java 16]]".
*/
def palantirformatJvmArgs: T[Seq[String]] = T {
Seq(
"--add-exports",
"jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
"--add-exports",
"jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"--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"
)
}

/**
* Path to options file for Palantir Java Format CLI. Defaults to `millSourcePath` `/` `palantirformat.options`.
*/
def palantirformatOptions: T[PathRef] = Task.Source(
millSourcePath / "palantirformat.options"
)

/**
* Palantir Java Format version. Defaults to `2.50.0`.
*/
def palantirformatVersion: T[String] = T {
"2.50.0"
}
}

/**
* Formats Java source files using [[https://github.com/palantir/palantir-java-format Palantir Java Format]].
*/
trait PalantirFormatModule extends JavaModule with PalantirFormatBaseModule {

/**
* Formats Java source files.
*
* @param check if an exception should be raised when formatting errors are found
* - when set, files are not formatted
* @param sources list of file or folder path(s) to be processed
* - path must be relative to [[millSourcePath]]
* - when empty, all [[sources]] are processed
*/
def palantirformat(
check: mainargs.Flag = mainargs.Flag(value = false),
sources: mainargs.Leftover[String]
): Command[Unit] = Task.Command {

val _sources =
if (sources.value.isEmpty) this.sources()
else sources.value.iterator.map(rel => PathRef(millSourcePath / os.RelPath(rel)))

PalantirFormatModule.palantirAction(
_sources,
check.value,
palantirformatOptions(),
palantirformatClasspath(),
palantirformatJvmArgs()
)
}
}
object PalantirFormatModule extends ExternalModule with PalantirFormatBaseModule with TaskModule {

override def defaultCommandName(): String = "formatAll"

/**
* Formats Java source files.
*
* @param check if an exception should be raised when formatting errors are found
* - when set, files are not formatted
* @param sources list of [[JavaModule]] sources to process
*/
def formatAll(
check: mainargs.Flag = mainargs.Flag(value = false),
@mainargs.arg(positional = true) sources: Tasks[Seq[PathRef]]
): Command[Unit] = Task.Command {

val _sources = T.sequence(sources.value)().iterator.flatten

palantirAction(
_sources,
check.value,
palantirformatOptions(),
palantirformatClasspath(),
palantirformatJvmArgs()
)
}

lazy val millDiscover: Discover = Discover[this.type]

private[palantirformat] def palantirAction(
sources: IterableOnce[PathRef],
check: Boolean,
options: PathRef,
classPath: Loose.Agg[PathRef],
jvmArgs: Seq[String]
)(implicit ctx: api.Ctx): Unit = {

if (check) {
ctx.log.info("checking format in java sources ...")
} else {
ctx.log.info("formatting java sources ...")
}

val mainArgs = palantirArgs(sources, check, options)

ctx.log.debug(s"running palantirformat with $mainArgs")

val exitCode = Jvm.callSubprocess(
mainClass = "com.palantir.javaformat.java.Main",
classPath = classPath.map(_.path),
jvmArgs = jvmArgs,
mainArgs = mainArgs,
workingDir = ctx.dest,
check = false
).exitCode

if (check && exitCode != 0) {
ctx.log.error(
"palantirformat aborted due to format error(s) (or invalid plugin settings/palantirformat options)"
)
throw new RuntimeException(s"palantirformat exit($exitCode)")
}
}

private def palantirArgs(
sources: IterableOnce[PathRef],
check: Boolean,
options: PathRef
): Seq[String] = {

val args = Seq.newBuilder[String]

// https://github.com/palantir/palantir-java-format/blob/dae9be4b84e2bd4d7ea346c6374fda47eee7118f/palantir-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java#L199
if (os.exists(options.path)) args += s"@${options.path}"

// https://github.com/palantir/palantir-java-format/blob/dae9be4b84e2bd4d7ea346c6374fda47eee7118f/palantir-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptions.java#L27
if (check) {
// do not overwrite files and exit(1) if formatting changes were detected
args += "--dry-run" += "--set-exit-if-changed"
} else {
// format in place
args += "--replace"
}

// https://github.com/palantir/palantir-java-format/blob/dae9be4b84e2bd4d7ea346c6374fda47eee7118f/palantir-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java#L49
args ++=
sources
.iterator
.map(_.path)
.flatMap(os.walk(_, includeTarget = true))
.filter(os.isFile)
.filter(_.ext == "java")
.map(_.toString())

args.result()
}

/**
* Path to options file for Palantir Java Format CLI at `T.workspace` `/` `palantirformat.options`.
*/
override def palantirformatOptions: T[PathRef] = Task.Source(
T.workspace / "palantirformat.options"
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
public class A {

public static void main(String[] args) {
System.out.println("hello"); // indentation should be fixed
}
}
76 changes: 76 additions & 0 deletions scalalib/test/resources/javalib/palantirformat/after/google/A.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.google.googlejavaformat.java.test;

/**
* Tests for AbstractTypeDeclarations, AnnotationTypeDeclarations, AnnotationTypeMemberDeclarations,
* Annotations, AnonymousClassDeclarations, ArrayAccesses, ArrayCreations, ArrayInitializers,
* ArrayTypes, AssertStatements, and Assignments.
*/
class A {
public @interface X {
int x();

int y() default
1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
+ 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1;
}

@X(x = 1)
private @interface Y {}

// TODO(jdd): Add annotation declaration with empty body.

@X(x = 1)
@Y
protected @interface Z {}

// TODO(jdd): Include type annotations once we can include a higher language level.

int[] array1 = new int[5];
int[] array2 = new int[] {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31
};
int[] array3 = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31
};
int[][] array4 = {
{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, {18, 19},
{20, 21}, {22, 23}
};
int[][][] arrayWithLongName = new int
[0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0
+ 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0]
[0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0
+ 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0]
[];

A a1 = new A() {
int x = array1[
0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0
+ 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0]
+ array2[
0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0
+ 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0]
+ array3[
0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0
+ 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0]
+ array4[0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0][
0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0];
};

void f(int something) {
assert 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
== 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1;
assert 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
== 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
: "that was certainly unexpected!";
arrayWithLongName[
0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0] =
arrayWithLongName[
0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0];
something = 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2
+ 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2
+ 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.google.googlejavaformat.java.test

object A {

// plugin should ignore non Java file
// introduce a compile error to trigger failure in case this file is processed
//}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package a;

// imports should be sorted
// unsed imports should be removed
import some.Configuration;
import some.GradleException;
import some.MavenPublication;
import some.Project;
import some.PublishingExtension;
import some.VariantVersionMappingStrategy;

public class Main {

private static void configureResolvedVersionsWithVersionMapping(Project project) {
project.getPluginManager().withPlugin("maven-publish", plugin -> {
project.getExtensions()
.getByType(PublishingExtension.class)
.getPublications()
.withType(MavenPublication.class)
.configureEach(publication -> publication.versionMapping(mapping -> {
mapping.allVariants(VariantVersionMappingStrategy::fromResolutionResult);
}));
});
}

private static GradleException notFound(String group, String name, Configuration configuration) {
String actual = configuration.getIncoming().getResolutionResult().getAllComponents().stream()
.map(ResolvedComponentResult::getModuleVersion)
.map(mvi -> String.format("\t- %s:%s:%s", mvi.getGroup(), mvi.getName(), mvi.getVersion()))
.collect(Collectors.joining("\n"));
// ...
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
public class A {

public static void main(String[] args) {
System.out.println("hello"); // indentation should be fixed
}
}
Loading
Loading