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

Add Scala Native support #2846

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
33 changes: 31 additions & 2 deletions .github/workflows/ci.yml
Expand Up @@ -29,9 +29,38 @@ 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: [macOS-latest, ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: olafurpg/setup-scala@v13
with:
java-version: adopt@1.8
- if: matrix.os == 'windows-latest'
name: setup windows environment
run: ./bin/scala-native-setup/windows-setup.sh
- if: matrix.os == 'macOS-latest'
name: setup macOS environment
run: ./bin/scala-native-setup/macos-setup.sh
- if: matrix.os == 'ubuntu-latest'
name: setup ubuntu environment
run: ./bin/scala-native-setup/ubuntu-setup.sh
- name: run tests
run: |
git fetch --tags -f
# 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
formatting:
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions bin/scala-native-setup/macos-setup.sh
@@ -0,0 +1,3 @@
apt-get install -y clang-12.0
PATH=/usr/lib/llvm-12.0/bin:${PATH}
clang --version
3 changes: 3 additions & 0 deletions bin/scala-native-setup/ubuntu-setup.sh
@@ -0,0 +1,3 @@
apt-get install -y clang-12.0
PATH=/usr/lib/llvm-12.0/bin:${PATH}
clang --version
3 changes: 3 additions & 0 deletions bin/scala-native-setup/windows-setup.sh
@@ -0,0 +1,3 @@
choco install llvm
echo "${env:ProgramFiles}\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
clang --version
123 changes: 82 additions & 41 deletions build.sbt
@@ -1,5 +1,6 @@
import Dependencies._
import sbtcrossproject.CrossPlugin.autoImport.crossProject
import scala.scalanative.build._

def parseTagVersion: String = {
import scala.sys.process._
Expand Down Expand Up @@ -37,11 +38,6 @@ inThisBuild(
Resolver.sonatypeRepo("releases"),
Resolver.sonatypeRepo("snapshots")
),
libraryDependencies ++= List(
munit.value % Test,
scalacheck % Test,
scalametaTestkit % Test
),
testFrameworks += new TestFramework("munit.Framework")
)
)
Expand All @@ -50,8 +46,9 @@ 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 @@ -64,7 +61,18 @@ commands += Command.command("ci-test") { s =>
s
}

lazy val dynamic = project
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",
Expand All @@ -76,14 +84,23 @@ lazy val dynamic = project
"io.get-coursier" % "interface" % "0.0.17",
"com.typesafe" % "config" % "1.4.1",
munit.value % Test,
scalametaTestkit % Test
scalametaTestkit.value % Test
),
scalacOptions ++= scalacJvmOptions.value
)
.nativeSettings(
libraryDependencies ++= List(
"org.ekrich" %%% "sjavatime" % "1.1.5"
),
scalaNativeNativeConfig
)
.dependsOn(interfaces)
.enablePlugins(BuildInfoPlugin)

lazy val interfaces = project
lazy val dynamicNative = dynamic.native

lazy val interfaces = crossProject(JVMPlatform, NativePlatform)
.withoutSuffixFor(JVMPlatform)
.in(file("scalafmt-interfaces"))
.settings(
moduleName := "scalafmt-interfaces",
Expand All @@ -100,14 +117,16 @@ lazy val interfaces = project
}
)

lazy val core = crossProject(JVMPlatform)
lazy val interfacesNative = interfaces.native

lazy val core = crossProject(JVMPlatform, NativePlatform)
.withoutSuffixFor(JVMPlatform)
.in(file("scalafmt-core"))
.settings(
moduleName := "scalafmt-core",
buildInfoSettings,
scalacOptions ++= scalacJvmOptions.value,
libraryDependencies ++= Seq(
metaconfig.value,
scalameta.value,
// scala-reflect is an undeclared dependency of fansi, see #1252.
// Scalafmt itself does not require scala-reflect.
Expand All @@ -128,21 +147,22 @@ lazy val core = crossProject(JVMPlatform)
}
}
)
// .jsSettings(
// libraryDependencies ++= List(
// metaconfigHocon.value,
// scalatest.value % Test // must be here for coreJS/test to run anything
// )
// )
.nativeSettings(
libraryDependencies ++= List(
metaconfigSconfig.value
),
scalaNativeNativeConfig
)
.jvmSettings(
Test / run / fork := true,
libraryDependencies ++= List(
metaconfigTypesafe.value
metaconfigTypesafe.value,
metaconfig.value
)
)
.enablePlugins(BuildInfoPlugin)
lazy val coreJVM = core.jvm
// lazy val coreJS = core.js

lazy val coreNative = core.native

import sbtassembly.AssemblyPlugin.defaultUniversalScript

Expand All @@ -160,10 +180,26 @@ val scalacJvmOptions = Def.setting {
}
}

lazy val cli = project
lazy val scalaNativeNativeConfig =
nativeConfig ~= {
_.withMode(Mode.debug)
.withLinkStubs(true)
}

lazy val cli = crossProject(JVMPlatform, NativePlatform)
.withoutSuffixFor(JVMPlatform)
.in(file("scalafmt-cli"))
.settings(
moduleName := "scalafmt-cli",
libraryDependencies ++= Seq(
"com.github.scopt" %%% "scopt" % "4.0.1",
// undeclared transitive dependency of coursier-small
"org.scala-lang.modules" %% "scala-xml" % "1.3.0"
),
scalacOptions ++= scalacJvmOptions.value,
Compile / mainClass := Some("org.scalafmt.cli.Cli")
)
.jvmSettings(
assembly / mainClass := Some("org.scalafmt.cli.Cli"),
assembly / assemblyOption := (assembly / assemblyOption).value
.withPrependShellScript(Some(defaultUniversalScript(shebang = false))),
Expand All @@ -174,15 +210,6 @@ lazy val cli = project
val oldStrategy = (assembly / assemblyMergeStrategy).value
oldStrategy(x)
},
libraryDependencies ++= Seq(
"com.googlecode.java-diff-utils" % "diffutils" % "1.3.0",
"com.martiansoftware" % "nailgun-server" % "0.9.1",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this dependency remains. why not keep it here, instead pushing down?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's probably my bad, experimented a lot with the project and kind of left a mess.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i must have misunderstood what that no suffix call does. just wanted to make sure this is correct.

"com.github.scopt" %% "scopt" % "4.0.1",
// undeclared transitive dependency of coursier-small
"org.scala-lang.modules" %% "scala-xml" % "1.3.0"
),
scalacOptions ++= scalacJvmOptions.value,
Compile / mainClass := Some("org.scalafmt.cli.Cli"),
nativeImageVersion := "20.1.0",
nativeImageOptions ++= {
sys.env
Expand All @@ -195,38 +222,52 @@ lazy val cli = project
.filter(identity)
.map(_ => "--static")
.toSeq
}
},
libraryDependencies += "com.martiansoftware" % "nailgun-server" % "0.9.1"
)
.nativeSettings(
scalaNativeNativeConfig
)
.dependsOn(coreJVM, dynamic)
.dependsOn(core, dynamic)
.enablePlugins(NativeImagePlugin)

lazy val tests = project
lazy val cliNative = cli.native

lazy val tests = crossProject(JVMPlatform, NativePlatform)
.withoutSuffixFor(JVMPlatform)
.in(file("scalafmt-tests"))
.settings(
publish / skip := true,
libraryDependencies ++= Seq(
// Test dependencies
"com.lihaoyi" %% "scalatags" % "0.10.0",
scalametaTestkit,
"com.lihaoyi" %%% "scalatags" % "0.10.0",
scalametaTestkit.value,
munit.value
),
scalacOptions ++= scalacJvmOptions.value,
javaOptions += "-Dfile.encoding=UTF8",
buildInfoPackage := "org.scalafmt.tests",
buildInfoKeys := Seq[BuildInfoKey](
"resourceDirectory" -> (Test / resourceDirectory).value
"resourceDirectory" -> (baseDirectory.value / ".." / "shared" / "src" / "test" / "resources")
)
)
.enablePlugins(BuildInfoPlugin)
.dependsOn(coreJVM, dynamic, cli)
.dependsOn(core, dynamic, cli)
.nativeSettings(
scalaNativeNativeConfig
)
.jvmSettings(
javaOptions += "-Dfile.encoding=UTF8"
)

lazy val testsNative = tests.native

lazy val benchmarks = project
.in(file("scalafmt-benchmarks"))
.settings(
publish / skip := true,
moduleName := "scalafmt-benchmarks",
libraryDependencies ++= Seq(
scalametaTestkit
scalametaTestkit.value
),
run / javaOptions ++= Seq(
"-Djava.net.preferIPv4Stack=true",
Expand All @@ -245,7 +286,7 @@ lazy val benchmarks = project
"-server"
)
)
.dependsOn(coreJVM)
.dependsOn(core.jvm)
.enablePlugins(JmhPlugin)

lazy val docs = project
Expand All @@ -255,7 +296,7 @@ lazy val docs = project
publish / skip := true,
mdoc := (Compile / run).evaluated
)
.dependsOn(cli, dynamic)
.dependsOn(cli.jvm, dynamic.jvm)
.enablePlugins(DocusaurusPlugin)

val V = "\\d+\\.\\d+\\.\\d+"
Expand Down
2 changes: 1 addition & 1 deletion docs/contributing-scalafmt.md
Expand Up @@ -81,7 +81,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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i assumed that no suffix for jvm meant perhaps that jvm wouldn't be added here...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately that's how cross building works in this case. We could probably override it for jvm?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No suffix only works for for the sbt commands I think, so f.e. in the cross project instead of cliJVM/run we only need to run cli/run for the JVM platform in this branch. The idea was to keep the previously used commands intact.


## Random stuff

Expand Down
2 changes: 1 addition & 1 deletion docs/installation.md
Expand Up @@ -466,7 +466,7 @@ handle parse and config errors.

Here is an example how to extend `ScalafmtReporter`.

```scala mdoc:file:scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ConsoleScalafmtReporter.scala
```scala mdoc:file:scalafmt-dynamic/shared/src/main/scala/org/scalafmt/dynamic/ConsoleScalafmtReporter.scala

```

Expand Down
3 changes: 2 additions & 1 deletion project/Dependencies.scala
Expand Up @@ -18,13 +18,14 @@ 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)
val scalameta = Def.setting("org.scalameta" %%% "scalameta" % scalametaV excludeAll scalapb.value)

val metaconfig = Def.setting("com.geirsson" %%% "metaconfig-core" % metaconfigV)
val metaconfigSconfig = Def.setting("com.geirsson" %%% "metaconfig-sconfig" % metaconfigV)
val metaconfigTypesafe = Def.setting("com.geirsson" %%% "metaconfig-typesafe-config" % metaconfigV)
val metaconfigHocon = Def.setting("com.geirsson" %%% "metaconfig-hocon" % metaconfigV)

Expand Down
3 changes: 3 additions & 0 deletions project/plugins.sbt
Expand Up @@ -15,3 +15,6 @@ addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.1.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.7.1")
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.0.1")
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.0")

addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.0.0")
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.1")
31 changes: 31 additions & 0 deletions scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/CliUtils.scala
@@ -0,0 +1,31 @@
package org.scalafmt.cli

import com.martiansoftware.nailgun.NGContext
import org.scalafmt.util.AbsoluteFile

trait CliUtils { this: Cli =>

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

protected val isNative: Boolean = false
}
13 changes: 13 additions & 0 deletions scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/TermUtils.scala
@@ -0,0 +1,13 @@
package org.scalafmt.cli

import java.sql.Timestamp

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
}