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 24, 2024
1 parent c39eee8 commit b2864f4
Show file tree
Hide file tree
Showing 37 changed files with 555 additions and 137 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 "scalafmt@scalameta.org" && 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 "scalafmt@scalameta.org" && 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
102 changes: 60 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.0")
.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(scalaNativeNativeConfig).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,7 @@ lazy val buildInfoSettings: Seq[Def.Setting[_]] = Seq(
buildInfoPackage := "org.scalafmt",
buildInfoObject := "Versions",
)

lazy val scalaNativeNativeConfig = 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)

}
2 changes: 2 additions & 0 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0")
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2")
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.4")
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.10.4")
addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2")
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)
}
36 changes: 36 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,36 @@
package org.scalafmt.cli

import org.scalafmt.sysops.AbsoluteFile

import com.martiansoftware.nailgun.NGContext

trait CliUtils {
protected val isNative: 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 returnDynamicRunner(): Either[String, ScalafmtRunner] =
Right(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
}
10 changes: 10 additions & 0 deletions scalafmt-cli/native/src/main/scala/org/scalafmt/cli/CliUtils.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.scalafmt.cli

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

protected def returnDynamicRunner(): Either[String, ScalafmtRunner] = {
assert(false, "Code path should be unreachable.")
???
}
}
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 b2864f4

Please sign in to comment.