Skip to content

Commit

Permalink
Support Scala Native 0.5.0-RC1 (#3054)
Browse files Browse the repository at this point in the history
Scala Native `0.5.0-RC1` was just released:
https://github.com/scala-native/scala-native/releases/tag/v0.5.0-RC1

Pull Request: #3054
  • Loading branch information
lolgab authored Mar 2, 2024
1 parent c8d50ad commit 5d7a3ea
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 25 deletions.
23 changes: 20 additions & 3 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ object Deps {
val scalanativeTestRunner = ivy"org.scala-native::test-runner:${scalanativeVersion}"
}

object Scalanative_0_5 {
val scalanativeVersion = "0.5.0-RC1"
val scalanativeTools = ivy"org.scala-native::tools:${scalanativeVersion}"
val scalanativeUtil = ivy"org.scala-native::util:${scalanativeVersion}"
val scalanativeNir = ivy"org.scala-native::nir:${scalanativeVersion}"
val scalanativeTestRunner = ivy"org.scala-native::test-runner:${scalanativeVersion}"
}

trait Play {
def playVersion: String
def playBinVersion: String = playVersion.split("[.]").take(2).mkString(".")
Expand Down Expand Up @@ -395,7 +403,8 @@ trait MillBaseTestsModule extends MillJavaModule with TestModule {
s"-DTEST_SCALA_3_2_VERSION=${Deps.testScala32Version}",
s"-DTEST_SCALA_3_3_VERSION=${Deps.testScala33Version}",
s"-DTEST_SCALAJS_VERSION=${Deps.Scalajs_1.scalaJsVersion}",
s"-DTEST_SCALANATIVE_VERSION=${Deps.Scalanative_0_4.scalanativeVersion}",
s"-DTEST_SCALANATIVE_0_4_VERSION=${Deps.Scalanative_0_4.scalanativeVersion}",
s"-DTEST_SCALANATIVE_0_5_VERSION=${Deps.Scalanative_0_5.scalanativeVersion}",
s"-DTEST_UTEST_VERSION=${Deps.utest.dep.version}",
s"-DTEST_SCALATEST_VERSION=${Deps.TestDeps.scalaTest.dep.version}",
s"-DTEST_TEST_INTERFACE_VERSION=${Deps.sbtTestInterface.dep.version}",
Expand Down Expand Up @@ -948,20 +957,28 @@ object contrib extends Module {

object scalanativelib extends MillStableScalaModule {
def moduleDeps = Seq(scalalib, scalanativelib.`worker-api`)
def testTransitiveDeps = super.testTransitiveDeps() ++ Seq(worker("0.4").testDep())
def testTransitiveDeps = super.testTransitiveDeps() ++ Seq(worker("0.4").testDep(), worker("0.5").testDep())

object `worker-api` extends MillPublishScalaModule {
def ivyDeps = Agg(Deps.sbtTestInterface)
}

object worker extends Cross[WorkerModule]("0.4")
object worker extends Cross[WorkerModule]("0.4", "0.5")

trait WorkerModule extends MillPublishScalaModule with Cross.Module[String] {
def scalaNativeWorkerVersion = crossValue
def millSourcePath: os.Path = super.millSourcePath / scalaNativeWorkerVersion
def testDepPaths = T { Seq(compile().classes) }
def moduleDeps = Seq(scalanativelib.`worker-api`)
def ivyDeps = scalaNativeWorkerVersion match {
case "0.5" =>
Agg(
Deps.osLib,
Deps.Scalanative_0_5.scalanativeTools,
Deps.Scalanative_0_5.scalanativeUtil,
Deps.Scalanative_0_5.scalanativeNir,
Deps.Scalanative_0_5.scalanativeTestRunner
)
case "0.4" =>
Agg(
Deps.osLib,
Expand Down
2 changes: 1 addition & 1 deletion contrib/bloop/test/src/mill/contrib/bloop/BloopTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ object BloopTests extends TestSuite {
val sv = sys.props.getOrElse("TEST_SCALA_2_13_VERSION", ???)
override def skipBloop: Boolean = isWin
override def scalaVersion = sv
override def scalaNativeVersion = sys.props.getOrElse("TEST_SCALANATIVE_VERSION", ???)
override def scalaNativeVersion = sys.props.getOrElse("TEST_SCALANATIVE_0_4_VERSION", ???)
override def releaseMode = T(ReleaseMode.Debug)
}

Expand Down
12 changes: 8 additions & 4 deletions scalanativelib/src/mill/scalanativelib/ScalaNativeModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,15 @@ trait ScalaNativeModule extends ScalaModule { outer =>
}

def nativeIvyDeps: T[Agg[Dep]] = T {
val scalaVersionSpecific =
val scalaVersionSpecific = {
val version =
if (scalaNativeVersion().startsWith("0.4")) scalaNativeVersion()
else s"${scalaVersion()}+${scalaNativeVersion()}"

if (ZincWorkerUtil.isScala3(scalaVersion()))
Agg(ivy"org.scala-native::scala3lib::${scalaNativeVersion()}")
else
Agg(ivy"org.scala-native::scalalib::${scalaNativeVersion()}")
Agg(ivy"org.scala-native::scala3lib::$version")
else Agg(ivy"org.scala-native::scalalib::$version")
}

Agg(
ivy"org.scala-native::nativelib::${scalaNativeVersion()}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import utest._
object ArgsParserTests extends TestSuite {

def tests: Tests = Tests {
'one - {
"one" - {
val result = ArgsParser.parse("hello:world")
assert(
result.length == 2,
result == Seq("hello", "world")
)
}
'two - { // we fail this test to check testing in scala.js
"two" - { // we fail this test to check testing in Scala Native
val result = ArgsParser.parse("hello:world")
assert(
result.length == 80
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import utest._
object MainTests extends TestSuite {

val tests: Tests = Tests {
'vmName - {
'containNative - {
"vmName" - {
"containNative" - {
assert(
Main.vmName.contains("Native")
)
}
'containScala - {
"containScala" - {
assert(
Main.vmName.contains("Scala")
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,25 @@ object HelloNativeWorldTests extends TestSuite {
override def mainClass = Some("hello.Main")
}

val scala213 = "2.13.6"
val scalaNative04 = "0.4.2"
val scala213 = sys.props.getOrElse("TEST_SCALA_2_13_VERSION", ???)
val scala31 = sys.props.getOrElse("TEST_SCALA_3_1_VERSION", ???)
val scala33 = sys.props.getOrElse("TEST_SCALA_3_3_VERSION", ???)
val scalaNative04Old = "0.4.2"
val scalaNative04 = sys.props.getOrElse("TEST_SCALANATIVE_0_4_VERSION", ???)
val scalaNative05 = sys.props.getOrElse("TEST_SCALANATIVE_0_5_VERSION", ???)
val utestVersion = sys.props.getOrElse("TEST_UTEST_VERSION", ???)

object HelloNativeWorld extends TestUtil.BaseModule {
implicit object ReleaseModeToSegments
extends Cross.ToSegments[ReleaseMode](v => List(v.toString))

val matrix = for {
scala <- Seq("3.2.1", "3.1.3", scala213, "2.12.13", "2.11.12")
scalaNative <- Seq(scalaNative04, "0.4.9")
scala <- Seq(scala33, scala31, scala213, "2.12.13", "2.11.12")
scalaNative <- Seq(scalaNative04Old, scalaNative04, scalaNative05)
mode <- List(ReleaseMode.Debug, ReleaseMode.ReleaseFast)
if !(ZincWorkerUtil.isScala3(scala) && scalaNative == scalaNative04)
if !(ZincWorkerUtil.isScala3(scala) && scalaNative == scalaNative04Old)
if !(scala.startsWith("2.11") && scalaNative != scalaNative04Old)
if !(scala.startsWith("2.12") && scalaNative == scalaNative05)
} yield (scala, scalaNative, mode)

object helloNativeWorld extends Cross[RootModule](matrix)
Expand All @@ -64,7 +71,7 @@ object HelloNativeWorldTests extends TestSuite {
object test extends ScalaNativeTests with TestModule.Utest {
override def sources = T.sources { millSourcePath / "src" / "utest" }
override def ivyDeps = super.ivyDeps() ++ Agg(
ivy"com.lihaoyi::utest::0.7.6"
ivy"com.lihaoyi::utest::$utestVersion"
)
}
}
Expand Down Expand Up @@ -129,7 +136,7 @@ object HelloNativeWorldTests extends TestSuite {
val Right((result, evalCount)) =
helloWorldEvaluator(HelloNativeWorld.helloNativeWorld(
scala213,
scalaNative04,
scalaNative04Old,
ReleaseMode.Debug
).jar)
val jar = result.path
Expand All @@ -155,7 +162,7 @@ object HelloNativeWorldTests extends TestSuite {
}
"artifactId_040" - testArtifactId(
scala213,
scalaNative04,
scalaNative04Old,
ReleaseMode.Debug,
"hello-native-world_native0.4_2.13"
)
Expand Down Expand Up @@ -202,14 +209,18 @@ object HelloNativeWorldTests extends TestSuite {

testAllMatrix(
(scala, scalaNative, releaseMode) => checkUtest(scala, scalaNative, releaseMode, cached),
skipScala = ZincWorkerUtil.isScala3 // Remove this once utest is released for Scala 3
skipScalaNative = v =>
v == scalaNative04Old ||
v.startsWith("0.5.") // Remove this once utest is released for Scala Native 0.5
)
}
"testCached" - {
val cached = true
testAllMatrix(
(scala, scalaNative, releaseMode) => checkUtest(scala, scalaNative, releaseMode, cached),
skipScala = ZincWorkerUtil.isScala3 // Remove this once utest is released for Scala 3
skipScalaNative = v =>
v == scalaNative04Old ||
v.startsWith("0.5.") // Remove this once utest is released for Scala Native 0.5
)
}

Expand Down Expand Up @@ -262,7 +273,7 @@ object HelloNativeWorldTests extends TestSuite {
)

val scalaNativeVersionSpecific =
if (scalaNativeVersion == scalaNative04) Set.empty
if (scalaNativeVersion == scalaNative04Old) Set.empty
else Set("Main.nir", "ArgsParser.nir")

common ++ scalaVersionSpecific ++ scalaNativeVersionSpecific
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ object ScalaTestsErrorTests extends TestSuite {
object ScalaTestsError extends TestUtil.BaseModule {
object scalaTestsError extends ScalaNativeModule {
def scalaVersion = sys.props.getOrElse("TEST_SCALA_3_3_VERSION", ???)
def scalaNativeVersion = sys.props.getOrElse("TEST_SCALANATIVE_VERSION", ???)
def scalaNativeVersion = sys.props.getOrElse("TEST_SCALANATIVE_0_4_VERSION", ???)
object test extends ScalaTests with TestModule.Utest
object testDisabledError extends ScalaTests with TestModule.Utest {
override def hierarchyChecks(): Unit = {}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package mill.scalanativelib.worker

import java.io.File
import java.lang.System.{err, out}

import mill.scalanativelib.worker.api._
import scala.scalanative.util.Scope
import scala.scalanative.build.{
Build,
BuildTarget => ScalaNativeBuildTarget,
Config,
Discover,
GC,
Logger,
LTO,
Mode,
NativeConfig => ScalaNativeNativeConfig
}
import scala.scalanative.testinterface.adapter.TestAdapter

import scala.concurrent.Await
import scala.concurrent.duration.Duration
import scala.concurrent.ExecutionContext.Implicits.global
import java.nio.file.Files

class ScalaNativeWorkerImpl extends mill.scalanativelib.worker.api.ScalaNativeWorkerApi {
implicit val scope: Scope = Scope.forever

def logger(level: NativeLogLevel): Logger =
Logger(
traceFn = msg => if (level.value >= NativeLogLevel.Trace.value) err.println(s"[trace] $msg"),
debugFn = msg => if (level.value >= NativeLogLevel.Debug.value) out.println(s"[debug] $msg"),
infoFn = msg => if (level.value >= NativeLogLevel.Info.value) out.println(s"[info] $msg"),
warnFn = msg => if (level.value >= NativeLogLevel.Warn.value) out.println(s"[warn] $msg"),
errorFn = msg => if (level.value >= NativeLogLevel.Error.value) err.println(s"[error] $msg")
)

def discoverClang(): File = Discover.clang().toFile
def discoverClangPP(): File = Discover.clangpp().toFile
def discoverCompileOptions(): Seq[String] = Discover.compileOptions()
def discoverLinkingOptions(): Seq[String] = Discover.linkingOptions()
def defaultGarbageCollector(): String = GC.default.name

def config(
mainClass: Either[String, String],
classpath: Seq[File],
nativeWorkdir: File,
nativeClang: File,
nativeClangPP: File,
nativeTarget: Option[String],
nativeCompileOptions: Seq[String],
nativeLinkingOptions: Seq[String],
nativeGC: String,
nativeLinkStubs: Boolean,
nativeLTO: String,
releaseMode: String,
nativeOptimize: Boolean,
nativeEmbedResources: Boolean,
nativeIncrementalCompilation: Boolean,
nativeDump: Boolean,
logLevel: NativeLogLevel,
buildTarget: BuildTarget
): Either[String, Config] = {
val nativeConfig =
ScalaNativeNativeConfig.empty
.withClang(nativeClang.toPath)
.withClangPP(nativeClangPP.toPath)
.withTargetTriple(nativeTarget)
.withCompileOptions(nativeCompileOptions)
.withLinkingOptions(nativeLinkingOptions)
.withGC(GC(nativeGC))
.withLinkStubs(nativeLinkStubs)
.withMode(Mode(releaseMode))
.withOptimize(nativeOptimize)
.withLTO(LTO(nativeLTO))
.withDump(nativeDump)
.withBuildTarget(buildTarget match {
case BuildTarget.Application => ScalaNativeBuildTarget.application
case BuildTarget.LibraryDynamic => ScalaNativeBuildTarget.libraryDynamic
case BuildTarget.LibraryStatic => ScalaNativeBuildTarget.libraryStatic
})
.withEmbedResources(nativeEmbedResources)
.withIncrementalCompilation(nativeIncrementalCompilation)
.withBaseName("out")

val config = Config.empty
.withClassPath(classpath.map(_.toPath))
.withBaseDir(nativeWorkdir.toPath)
.withCompilerConfig(nativeConfig)
.withLogger(logger(logLevel))

if (buildTarget == BuildTarget.Application) {
mainClass match {
case Left(error) =>
Left(error)
case Right(mainClass) =>
Right(config.withMainClass(Some(mainClass)))
}
} else Right(config)
}

def nativeLink(nativeConfig: Object, outDirectory: File): File = {
val config = nativeConfig.asInstanceOf[Config]

val result = Await.result(Build.buildCached(config), Duration.Inf)

val resultInOutDirectory =
Files.move(result, outDirectory.toPath().resolve(result.getFileName()))

resultInOutDirectory.toFile()
}

def getFramework(
testBinary: File,
envVars: Map[String, String],
logLevel: NativeLogLevel,
frameworkName: String
): (() => Unit, sbt.testing.Framework) = {
val config = TestAdapter.Config()
.withBinaryFile(testBinary)
.withEnvVars(envVars)
.withLogger(logger(logLevel))

val adapter = new TestAdapter(config)

(
() => adapter.close(),
adapter
.loadFrameworks(List(List(frameworkName)))
.flatten
.headOption
.getOrElse(throw new RuntimeException("Failed to get framework"))
)
}
}

0 comments on commit 5d7a3ea

Please sign in to comment.