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

Support Scala Native 0.5.0-RC1 #3054

Merged
merged 16 commits into from
Mar 2, 2024
Merged
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"))
)
}
}
lefou marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package scala.scalanative.build

object MillUtils {
def targetsWindows(config: Config): Boolean = config.targetsWindows
def targetsMac(config: Config): Boolean = config.targetsMac
}