Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,36 @@ and this [maven plugin](https://github.com/revelc/formatter-maven-plugin), thoug
Add the plugin to `project/plugins.sbt`:

```scala
// Default plugin:
addSbtPlugin("com.github.sbt" % "sbt-java-formatter" % --latest version---)

// Alternative for Java 17+: wraps formatter commands in a fresh sbt JVM,
// so you do not need to configure `--add-opens` manually, see below.
addSbtPlugin("com.github.sbt" % "sbt-java-formatter-add-opens" % --latest version---)
```

For available versions see [releases](https://github.com/sbt/sbt-java-formatter/releases).

The following commands are available:

* `javafmt` formats Java files
* `javafmtAll` formats Java files for all configurations (`Compile` and `Test` by default)
* `javafmtCheck` fails if files need reformatting
* `javafmtCheckAll` fails if files need reformatting in any configuration (`Compile` and `Test` by default)

The `sbt-java-formatter-add-opens` plugin wraps the above commands and, on Java 17+, runs them in a fresh sbt JVM with the required `jdk.compiler` module access flags. From a user perspective, the commands stay the same and no manual JVM flags need to be configured.

* The `javafmtOnCompile` setting controls whether the formatter kicks in on compile (`false` by default).
* The `javafmtStyle` setting defines the formatting style: Google Java Style (by default) or AOSP style.

This plugin requires sbt 1.3.0+.

## Java 17+

`google-java-format` relies on internal `jdk.compiler` APIs. On Java 17 and newer, access to those APIs is strongly encapsulated by the module system.

If you depend on `sbt-java-formatter-add-opens`, the formatter commands (`javafmt`, `javafmtAll`, `javafmtCheck`, `javafmtCheckAll`) automatically relaunch in a JVM with the required module flags, instead of requiring manual `-J--add-opens=...` setup.

## Enable in other scopes (eg `IntegrationTest`)

The sbt plugin is enabled by default for the `Test` and `Compile` configurations. Use `JavaFormatterPlugin.toBeScopedSettings` to enable the plugin for the `IntegrationTest` scope and then use `It/javafmt` to format.
Expand Down
88 changes: 50 additions & 38 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,63 @@ lazy val scala3 = "3.8.3"
ThisBuild / scalaVersion := scala212
ThisBuild / crossScalaVersions := Seq(scala212, scala3)

lazy val sbtJavaFormatter = project.in(file(".")).aggregate(plugin).settings(publish / skip := true)
def commonSettings: Seq[Setting[?]] = Seq(
homepage := scmInfo.value.map(_.browseUrl),
scmInfo := Some(
ScmInfo(url("https://github.com/sbt/sbt-java-formatter"), "scm:git:git@github.com:sbt/sbt-java-formatter.git")),
developers := List(
Developer("ktoso", "Konrad 'ktoso' Malawski", "<ktoso@project13.pl>", url("https://github.com/ktoso"))),
startYear := Some(2015),
description := "Formats Java code in your project.",
licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0.html")),
(pluginCrossBuild / sbtVersion) := {
scalaBinaryVersion.value match {
case "2.12" => "1.9.0"
case _ => "2.0.0-RC11"
}
},
scalacOptions ++= {
Vector("-encoding", "UTF-8", "-unchecked", "-deprecation", "-feature") ++ (scalaBinaryVersion.value match {
case "2.12" => Vector("-Xsource:3", "-release:11")
case _ => Vector("-Wconf:error")
})
},
javacOptions ++= Seq("-encoding", "UTF-8"),
scriptedLaunchOpts := {
scriptedLaunchOpts.value ++
Seq("-Xmx1024M", "-Dplugin.version=" + version.value)
},
scriptedLaunchOpts ++= {
if (scala.util.Properties.isJavaAtLeast("17")) {
Seq("api", "code", "file", "parser", "tree", "util").map { x =>
s"--add-exports=jdk.compiler/com.sun.tools.javac.${x}=ALL-UNNAMED"
}
} else {
Nil
}
},
scriptedBufferLog := false,
scalafmtOnCompile := true)

lazy val sbtJavaFormatter =
project.in(file(".")).aggregate(plugin).aggregate(`plugin-add-opens`).settings(publish / skip := true)

lazy val plugin = project
.in(file("plugin"))
.enablePlugins(SbtPlugin)
.enablePlugins(AutomateHeaderPlugin)
.settings(commonSettings *)
.settings(
name := "sbt-java-formatter",
homepage := scmInfo.value.map(_.browseUrl),
scmInfo := Some(
ScmInfo(url("https://github.com/sbt/sbt-java-formatter"), "scm:git:git@github.com:sbt/sbt-java-formatter.git")),
developers := List(
Developer("ktoso", "Konrad 'ktoso' Malawski", "<ktoso@project13.pl>", url("https://github.com/ktoso"))),
libraryDependencies ++= Seq("com.google.googlejavaformat" % "google-java-format" % "1.24.0"),
startYear := Some(2015),
description := "Formats Java code in your project.",
licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0.html")),
(pluginCrossBuild / sbtVersion) := {
scalaBinaryVersion.value match {
case "2.12" => "1.9.0"
case _ => "2.0.0-RC11"
}
},
scalacOptions ++= {
Vector("-encoding", "UTF-8", "-unchecked", "-deprecation", "-feature") ++ (scalaBinaryVersion.value match {
case "2.12" => Vector("-Xsource:3", "-release:11")
case _ => Vector("-Wconf:error")
})
},
javacOptions ++= Seq("-encoding", "UTF-8"),
scriptedLaunchOpts := {
scriptedLaunchOpts.value ++
Seq("-Xmx1024M", "-Dplugin.version=" + version.value)
},
scriptedLaunchOpts ++= {
if (scala.util.Properties.isJavaAtLeast("17")) {
Seq("api", "code", "file", "parser", "tree", "util").map { x =>
s"--add-exports=jdk.compiler/com.sun.tools.javac.${x}=ALL-UNNAMED"
}
} else {
Nil
}
},
scriptedBufferLog := false,
scalafmtOnCompile := true)
libraryDependencies ++= Seq("com.google.googlejavaformat" % "google-java-format" % "1.24.0"))

lazy val `plugin-add-opens` = project
.in(file("plugin-add-opens"))
.enablePlugins(SbtPlugin)
.enablePlugins(AutomateHeaderPlugin)
.settings(commonSettings *)
.settings(name := "sbt-java-formatter-add-opens")
.dependsOn(plugin)

ThisBuild / organization := "com.github.sbt"
ThisBuild / organizationName := "sbt community"
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2015 sbt community
*
* 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.github.sbt

import scala.sys.process.Process

import sbt._
import sbt.Keys._

//import com.github.sbt.JavaFormatterPlugin

object JavaFormatterWrapperPlugin extends AutoPlugin {
override def requires = JavaFormatterPlugin

override def trigger = allRequirements

override def globalSettings =
Seq(commands += javafmt, commands += javafmtCheck, commands += javafmtAll, commands += javafmtCheckAll)

private val javafmtWrapperProp = "play.javafmt.wrapper"

private val javafmtExports =
Seq("api", "code", "file", "parser", "tree", "util").map { exportedPackage =>
s"-J--add-opens=jdk.compiler/com.sun.tools.javac.${exportedPackage}=ALL-UNNAMED"
}

private def javafmtCommand(name: String, delegatedCommand: String): Command =
Command.command(
name,
Help.more(
name,
s"Runs $delegatedCommand in a fresh sbt JVM with the required jdk.compiler module-opening flags")) { state =>
if (sys.props.get(javafmtWrapperProp).contains("true")) {
delegatedCommand :: state
} else {
val extracted = Project.extract(state)
val base = extracted.get(ThisBuild / baseDirectory)
val sbtArgs = Seq("sbt", "--server", s"-D$javafmtWrapperProp=true") ++ javafmtExports ++ Seq(name)
val exitCode = Process(sbtArgs, base).!
if (exitCode == 0) state else state.fail
}
}

private val javafmt = javafmtCommand("javafmt", "javafmt")
private val javafmtCheck = javafmtCommand("javafmtCheck", "javafmtCheck")
private val javafmtAll = javafmtCommand("javafmtAll", "all javafmtAll")
private val javafmtCheckAll = javafmtCommand("javafmtCheckAll", "all javafmtCheckAll")
}