Skip to content

Commit

Permalink
Allow to compile with Scala Native
Browse files Browse the repository at this point in the history
Adds Regexes for scala native, splits up uses of certain
Collections, System.console, and metaconfig.
Also moves some tests to a JVM only directory.
  • Loading branch information
jchyb committed Sep 25, 2024
1 parent c39eee8 commit 1e10509
Show file tree
Hide file tree
Showing 37 changed files with 550 additions and 143 deletions.
27 changes: 25 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,32 @@ jobs:
- run:
# for GitOps tests
git config --global user.email "[email protected]" && git config --global user.name "scalafmt"
- run: TEST="2.12" sbt ci-test
- run: TEST="2.12" sbt ci-test-jvm
shell: bash
- run: TEST="2.13" sbt ci-test
- run: TEST="2.13" sbt ci-test-jvm
shell: bash
test-scala-native:
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JVM
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: 'sbt'
- run:
# for GitOps tests
git config --global user.email "[email protected]" && git config --global user.name "scalafmt"
- run: TEST="2.12" sbt ci-test-native
shell: bash
- run: TEST="2.13" sbt ci-test-native
shell: bash
community-test:
strategy:
Expand Down
100 changes: 58 additions & 42 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import java.nio.file.Paths

import scala.scalanative.build._

import Dependencies._
import sbtcrossproject.CrossPlugin.autoImport.crossProject

Expand Down Expand Up @@ -32,17 +34,16 @@ inThisBuild(List(
crossScalaVersions := List(scala213, scala212),
resolvers ++= Resolver.sonatypeOssRepos("releases"),
resolvers ++= Resolver.sonatypeOssRepos("snapshots"),
libraryDependencies ++=
List(munit.value % Test, scalacheck % Test, scalametaTestkit % Test),
testFrameworks += new TestFramework("munit.Framework"),
))

name := "scalafmtRoot"
publish / skip := true

addCommandAlias("native-image", "cli/nativeImage")
addCommandAlias("scala-native", "cliNative/compile;cliNative/nativeLink")

commands += Command.command("ci-test") { s =>
commands += Command.command("ci-test-jvm") { s =>
val scalaVersion = sys.env.get("TEST") match {
case Some("2.12") => scala212
case _ => scala213
Expand All @@ -52,8 +53,16 @@ commands += Command.command("ci-test") { s =>
docsTest :: s
}

lazy val dynamic = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform)
.in(file("scalafmt-dynamic")).settings(
commands += Command.command("ci-test-native") { s =>
val scalaVersion = sys.env.get("TEST") match {
case Some("2.12") => scala212
case _ => scala213
}
s"++$scalaVersion" :: "testsNative/test" :: s
}

lazy val dynamic = crossProject(JVMPlatform, NativePlatform)
.withoutSuffixFor(JVMPlatform).in(file("scalafmt-dynamic")).settings(
moduleName := "scalafmt-dynamic",
description := "Implementation of scalafmt-interfaces",
buildInfoSettings,
Expand All @@ -63,14 +72,14 @@ lazy val dynamic = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform)
"io.get-coursier" % "interface" % "0.0.17",
"com.typesafe" % "config" % "1.4.3",
munit.value % Test,
scalametaTestkit % Test,
scalametaTestkit.value % Test,
),
scalacOptions ++= scalacJvmOptions.value,
).dependsOn(interfaces, sysops).dependsOn(core % "test")
.enablePlugins(BuildInfoPlugin)

lazy val interfaces = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform)
.in(file("scalafmt-interfaces")).settings(
lazy val interfaces = crossProject(JVMPlatform, NativePlatform)
.withoutSuffixFor(JVMPlatform).in(file("scalafmt-interfaces")).settings(
moduleName := "scalafmt-interfaces",
description :=
"Dependency-free, pure Java public interfaces to integrate with Scalafmt through a build tool or editor plugin.",
Expand All @@ -86,8 +95,8 @@ lazy val interfaces = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform)
},
)

lazy val sysops = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform)
.in(file("scalafmt-sysops")).settings(
lazy val sysops = crossProject(JVMPlatform, NativePlatform)
.withoutSuffixFor(JVMPlatform).in(file("scalafmt-sysops")).settings(
moduleName := "scalafmt-sysops",
description := "Scalafmt systems operations",
scalacOptions ++= scalacJvmOptions.value,
Expand All @@ -102,45 +111,48 @@ lazy val sysops = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform)
},
)

lazy val config = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform)
.in(file("scalafmt-config")).settings(
lazy val config = crossProject(JVMPlatform, NativePlatform)
.withoutSuffixFor(JVMPlatform).in(file("scalafmt-config")).settings(
moduleName := "scalafmt-config",
description := "Scalafmt config parsing",
scalacOptions ++= scalacJvmOptions.value,
libraryDependencies ++= Seq(metaconfig.value),
).jvmSettings(libraryDependencies ++= Seq(metaconfigTypesafe.value))
.nativeSettings(libraryDependencies ++= Seq(metaconfigSconfig.value))
// .jsSettings(
// libraryDependencies ++= Seq(
// metaconfigHocon.value,
// )
// )

lazy val core = crossProject(JVMPlatform).in(file("scalafmt-core")).settings(
moduleName := "scalafmt-core",
buildInfoSettings,
scalacOptions ++= scalacJvmOptions.value,
libraryDependencies ++= Seq("org.scalameta" %% "mdoc-parser" % mdocV),
libraryDependencies ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, 13)) => Seq()
case _ => Seq(compilerPlugin(
"org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full,
))
}
},
)
lazy val core = crossProject(JVMPlatform, NativePlatform)
.in(file("scalafmt-core")).settings(
moduleName := "scalafmt-core",
buildInfoSettings,
scalacOptions ++= scalacJvmOptions.value,
libraryDependencies ++= Seq("org.scalameta" %%% "mdoc-parser" % mdocV),
libraryDependencies ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, 13)) => Seq()
case _ => Seq(compilerPlugin(
"org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full,
))
}
},
)
// .jsSettings(
// libraryDependencies ++= List(
// scalatest.value % Test // must be here for coreJS/test to run anything
// )
// )
.nativeSettings(libraryDependencies += "com.lihaoyi" %%% "fastparse" % "3.1.1")
.jvmSettings(Test / run / fork := true).dependsOn(sysops, config, macros)
.enablePlugins(BuildInfoPlugin)
lazy val coreJVM = core.jvm
// lazy val coreJS = core.js

lazy val macros = crossProject(JVMPlatform).in(file("scalafmt-macros"))
.settings(
lazy val macros = crossProject(JVMPlatform, NativePlatform)
.in(file("scalafmt-macros")).settings(
moduleName := "scalafmt-macros",
buildInfoSettings,
scalacOptions ++= scalacJvmOptions.value,
Expand All @@ -165,8 +177,8 @@ val scalacJvmOptions = Def.setting {
cross ++ unused
}

lazy val cli = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform)
.in(file("scalafmt-cli")).settings(
lazy val cli = crossProject(JVMPlatform, NativePlatform)
.withoutSuffixFor(JVMPlatform).in(file("scalafmt-cli")).settings(
moduleName := "scalafmt-cli",
assembly / mainClass := Some("org.scalafmt.cli.Cli"),
assembly / assemblyOption := (assembly / assemblyOption).value
Expand All @@ -179,9 +191,9 @@ lazy val cli = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform)
oldStrategy(x)
},
libraryDependencies ++= Seq(
"org.scalameta" %% "munit-diff" % "1.0.2",
"org.scalameta" %%% "munit-diff" % "1.0.2",
"com.martiansoftware" % "nailgun-server" % "0.9.1",
"com.github.scopt" %% "scopt" % "4.1.0",
"com.github.scopt" %%% "scopt" % "4.1.0",
),
scalacOptions ++= scalacJvmOptions.value,
Compile / mainClass := Some("org.scalafmt.cli.Cli"),
Expand All @@ -195,33 +207,35 @@ lazy val cli = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform)
val isStatic = sys.env.get("NATIVE_IMAGE_STATIC").exists(_.toBoolean)
if (isStatic) Seq("--static") else Nil
},
).dependsOn(core, dynamic).enablePlugins(NativeImagePlugin)
).nativeSettings(scalaNativeConfig).dependsOn(core, dynamic)
.enablePlugins(NativeImagePlugin)

lazy val tests = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform)
.in(file("scalafmt-tests")).settings(
lazy val tests = crossProject(JVMPlatform, NativePlatform)
.withoutSuffixFor(JVMPlatform).in(file("scalafmt-tests")).settings(
publish / skip := true,
libraryDependencies ++= Seq(
// Test dependencies
"com.lihaoyi" %% "scalatags" % "0.13.1",
scalametaTestkit,
"com.lihaoyi" %%% "scalatags" % "0.13.1",
scalametaTestkit.value,
munit.value,
),
scalacOptions ++= scalacJvmOptions.value,
javaOptions += "-Dfile.encoding=UTF8",
buildInfoPackage := "org.scalafmt.tests",
buildInfoKeys := Seq[BuildInfoKey]("resourceDirectory" -> {
val sharedTests = (baseDirectory.value.getParentFile / "shared").toPath
(Test / resourceDirectories).value.find(_.toPath.startsWith(sharedTests))
.get
}),
).enablePlugins(BuildInfoPlugin).dependsOn(core, dynamic, cli)
).enablePlugins(BuildInfoPlugin)
.jvmSettings(javaOptions += "-Dfile.encoding=UTF8")
.dependsOn(core, dynamic, cli)

lazy val communityTests = project.in(file("scalafmt-tests-community")).settings(
publish / skip := true,
libraryDependencies ++= Seq(
// Test dependencies
"com.lihaoyi" %% "scalatags" % "0.13.1",
scalametaTestkit,
"com.lihaoyi" %%% "scalatags" % "0.13.1",
scalametaTestkit.value,
munit.value,
),
scalacOptions ++= scalacJvmOptions.value,
Expand All @@ -232,7 +246,7 @@ lazy val communityTests = project.in(file("scalafmt-tests-community")).settings(
lazy val benchmarks = project.in(file("scalafmt-benchmarks")).settings(
publish / skip := true,
moduleName := "scalafmt-benchmarks",
libraryDependencies ++= Seq(scalametaTestkit),
libraryDependencies ++= Seq(scalametaTestkit.value),
run / javaOptions ++= Seq(
"-Djava.net.preferIPv4Stack=true",
"-XX:+AggressiveOpts",
Expand Down Expand Up @@ -284,3 +298,5 @@ lazy val buildInfoSettings: Seq[Def.Setting[_]] = Seq(
buildInfoPackage := "org.scalafmt",
buildInfoObject := "Versions",
)

lazy val scalaNativeConfig = nativeConfig ~= { _.withMode(Mode.releaseFull) }
2 changes: 1 addition & 1 deletion docs/contributing-scalafmt.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ To build a native image of the command-line interface using

- From the project root directory,
- run `sbt cli/assembly`
- run `java -jar scalafmt-cli/target/scala-2.13/scalafmt.jar`, to execute recently built artifacts
- run `java -jar scalafmt-cli/jvm/target/scala-2.13/scalafmt.jar`, to execute recently built artifacts

## Random stuff

Expand Down
5 changes: 3 additions & 2 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ object Dependencies {
)
}

val scalametaTestkit = "org.scalameta" %% "testkit" % scalametaV
val scalametaTestkit = Def.setting("org.scalameta" %%% "testkit" % scalametaV)

val scalacheck = "org.scalacheck" %% "scalacheck" % scalacheckV
val munit = Def.setting("org.scalameta" %%% "munit" % munitV)
Expand All @@ -31,6 +31,7 @@ object Dependencies {
val metaconfig = Def.setting("org.scalameta" %%% "metaconfig-core" % metaconfigV)
val metaconfigTypesafe = Def
.setting("org.scalameta" %%% "metaconfig-typesafe-config" % metaconfigV)
val metaconfigHocon = Def.setting("com.geirsson" %%% "metaconfig-hocon" % metaconfigV)
val metaconfigHocon = Def.setting("com.geirsson" %%% "metaconfig-hocon" % metaconfigV)
val metaconfigSconfig = Def.setting("org.scalameta" %%% "metaconfig-sconfig" % metaconfigV)

}
8 changes: 7 additions & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ resolvers ++= Seq(
Resolver.bintrayIvyRepo("jetbrains", "sbt-plugins"),
)

val crossProjectV = "1.3.2"

addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.6.1")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2")
addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.6.1")
Expand All @@ -12,6 +14,10 @@ addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.2.0")
addSbtPlugin("org.scalameta" % "sbt-native-image" % "0.3.4")
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.7")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0")
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2")
addSbtPlugin(
"org.portable-scala" % "sbt-scala-native-crossproject" % crossProjectV,
)
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % crossProjectV)
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.4")
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.10.4")
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.5")
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.scalafmt.cli

import java.io.PrintWriter

private[scalafmt] trait CliOptionsUtils {
def getConsoleWriter(): Option[PrintWriter] = Option(System.console())
.map(_.writer)
}
35 changes: 35 additions & 0 deletions scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/CliUtils.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.scalafmt.cli

import org.scalafmt.sysops.AbsoluteFile

import com.martiansoftware.nailgun.NGContext

private[scalafmt] trait CliUtils {
protected val isScalaNative: Boolean = false

def nailMain(nGContext: NGContext): Unit = {
val workingDirectory = AbsoluteFile.fromPathIfAbsolute(
nGContext.getWorkingDirectory,
).getOrElse {
throw new IllegalStateException(
s"Expected absolute path, " +
s"obtained nGContext.getWorkingDirectory = ${nGContext.getWorkingDirectory}",
)
}
val exit = Cli.mainWithOptions(
nGContext.getArgs,
CliOptions.default.copy(common =
CliOptions.default.common.copy(
cwd = Some(workingDirectory),
out = nGContext.out,
in = nGContext.in,
err = nGContext.err,
),
),
)
nGContext.exit(exit.code)
}

protected def getDynamicRunner(): ScalafmtRunner = ScalafmtDynamicRunner

}
13 changes: 13 additions & 0 deletions scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/TermUtils.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.scalafmt.cli

import java.sql.Timestamp

private[scalafmt] trait TermUtils {

// Copy/pasted over from coursier, but unused in scalafmt
private val format = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
protected def formatTimestamp(ts: Long): String = format
.format(new Timestamp(ts))

def noConsole = System.console() == null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.scalafmt.cli

import java.io.PrintWriter

private[scalafmt] trait CliOptionsUtils {
def getConsoleWriter(): Option[PrintWriter] = None
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.scalafmt.cli

private[scalafmt] trait CliUtils {
protected val isScalaNative: Boolean = true

protected def getDynamicRunner(): ScalafmtRunner = ???
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.scalafmt.cli

private[scalafmt] trait TermUtils {

// Copy/pasted over from coursier, but not used in scalafmt
protected def formatTimestamp(ts: Long): String = ???

def noConsole = false
}
Loading

0 comments on commit 1e10509

Please sign in to comment.