Skip to content

Commit f959111

Browse files
committed
Print a warning when Scala Native default version gets downgraded because of dependency limitations
1 parent 814c537 commit f959111

File tree

10 files changed

+109
-20
lines changed

10 files changed

+109
-20
lines changed

build.sc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,7 @@ trait CliIntegration extends SbtModule with ScalaCliPublishModule with HasTests
987987
| def defaultGraalVMJavaVersion = "${deps.graalVmJavaVersion}"
988988
| def defaultGraalVMVersion = "${deps.graalVmVersion}"
989989
| def scalaPyVersion = "${Deps.scalaPy.dep.version}"
990+
| def scalaPyMaxScalaNative = "${Deps.Versions.maxScalaNativeForScalaPy}"
990991
| def bloopVersion = "${Deps.bloopRifle.dep.version}"
991992
| def pprintVersion = "${TestDeps.pprint.dep.version}"
992993
| def munitVersion = "${TestDeps.munit.dep.version}"
@@ -1002,8 +1003,10 @@ trait CliIntegration extends SbtModule with ScalaCliPublishModule with HasTests
10021003
| def libsodiumVersion = "${deps.libsodiumVersion}"
10031004
| def dockerArchLinuxImage = "${TestDeps.archLinuxImage}"
10041005
|
1005-
| def toolkitVersion = "${Deps.toolkitVersion}"
1006-
| def typelevelToolkitVersion = "${Deps.typelevelToolkitVersion}"
1006+
| def toolkitVersion = "${Deps.toolkitVersion}"
1007+
| def toolkiMaxScalaNative = "${Deps.Versions.maxScalaNativeForToolkit}"
1008+
| def typelevelToolkitVersion = "${Deps.typelevelToolkitVersion}"
1009+
| def typelevelToolkitMaxScalaNative = "${Deps.Versions.maxScalaNativeForTypelevelToolkit}"
10071010
|
10081011
| def ghOrg = "$ghOrg"
10091012
| def ghName = "$ghName"

modules/cli/src/main/scala/scala/cli/commands/export0/Export.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,13 @@ object Export extends ScalaCommand[ExportOptions] {
152152
.copy(
153153
scalaNativeOptions = initialBuildOptions.scalaNativeOptions.copy(
154154
maxDefaultNativeVersions =
155-
if shouldExportToMill then List(Constants.maxScalaNativeForMillExport)
156-
else Nil
155+
initialBuildOptions.scalaNativeOptions.maxDefaultNativeVersions ++
156+
(if shouldExportToMill && Constants.scalaNativeVersion != Constants.maxScalaNativeForMillExport
157+
then
158+
val warningMsg =
159+
s"Mill export does not support Scala Native ${Constants.scalaNativeVersion}, ${Constants.maxScalaNativeForMillExport} should be used instead."
160+
List(Constants.maxScalaNativeForMillExport -> warningMsg)
161+
else Nil)
157162
),
158163
mainClass = options.mainClass.mainClass.filter(_.nonEmpty)
159164
)

modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import scala.build.internal.{Constants, FetchExternalBinary, OsLibc, Util}
2929
import scala.build.options.ScalaVersionUtil.fileWithTtl0
3030
import scala.build.options.{BuildOptions, ComputeVersion, Platform, ScalacOpt, ShadowingSeq}
3131
import scala.build.preprocessing.directives.ClasspathUtils.*
32-
import scala.build.preprocessing.directives.Toolkit
32+
import scala.build.preprocessing.directives.{Python, Toolkit}
3333
import scala.build.options as bo
3434
import scala.cli.ScalaCli
3535
import scala.cli.commands.publish.ConfigUtil.*
@@ -267,7 +267,7 @@ final case class SharedOptions(
267267

268268
private def scalaNativeOptions(
269269
opts: ScalaNativeOptions,
270-
maxDefaultScalaNativeVersions: List[String]
270+
maxDefaultScalaNativeVersions: List[(String, String)]
271271
): options.ScalaNativeOptions = {
272272
import opts._
273273
options.ScalaNativeOptions(
@@ -331,7 +331,8 @@ final case class SharedOptions(
331331
val (resolvedToolkitDependency, toolkitMaxDefaultScalaNativeVersions) =
332332
SharedOptions.resolveToolkitDependencyAndScalaNativeVersionReqs(withToolkit, logger)
333333
val scalapyMaxDefaultScalaNativeVersions =
334-
if sharedPython.python.contains(true) then List(Constants.scalaPyMaxScalaNative)
334+
if sharedPython.python.contains(true) then
335+
List(Constants.scalaPyMaxScalaNative -> Python.maxScalaNativeWarningMsg)
335336
else Nil
336337
val maxDefaultScalaNativeVersions =
337338
toolkitMaxDefaultScalaNativeVersions.toList ++ scalapyMaxDefaultScalaNativeVersions
@@ -741,7 +742,7 @@ object SharedOptions {
741742
private def resolveToolkitDependencyAndScalaNativeVersionReqs(
742743
toolkitVersion: Option[String],
743744
logger: Logger
744-
): (Seq[Positioned[AnyDependency]], Seq[String]) = {
745+
): (Seq[Positioned[AnyDependency]], Seq[(String, String)]) = {
745746
if (
746747
(toolkitVersion.contains("latest")
747748
|| toolkitVersion.contains(Toolkit.typelevel + ":latest")
@@ -761,9 +762,19 @@ object SharedOptions {
761762
val maxScalaNativeVersions =
762763
toolkitDefaults.flatMap {
763764
case Toolkit.ToolkitDefaults(isScalaToolkitDefault, isTypelevelToolkitDefault) =>
764-
val st = if (isScalaToolkitDefault) Seq(Constants.toolkitMaxScalaNative) else Nil
765+
val st = if (isScalaToolkitDefault)
766+
Seq(Constants.toolkitMaxScalaNative -> Toolkit.maxScalaNativeWarningMsg(
767+
"Scala Toolkit",
768+
Constants.toolkitMaxScalaNative
769+
))
770+
else Nil
765771
val tlt =
766-
if (isTypelevelToolkitDefault) Seq(Constants.typelevelToolkitMaxScalaNative) else Nil
772+
if (isTypelevelToolkitDefault)
773+
Seq(Constants.typelevelToolkitMaxScalaNative -> Toolkit.maxScalaNativeWarningMsg(
774+
"TypeLevel Toolkit",
775+
Constants.typelevelToolkitMaxScalaNative
776+
))
777+
else Nil
767778
st ++ tlt
768779
}
769780
dependencies -> maxScalaNativeVersions

modules/directives/src/main/scala/scala/build/preprocessing/directives/Python.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ final case class Python(
2020
python = Some(true)
2121
),
2222
scalaNativeOptions = ScalaNativeOptions(
23-
maxDefaultNativeVersions = List(Constants.scalaPyMaxScalaNative)
23+
maxDefaultNativeVersions =
24+
List(Constants.scalaPyMaxScalaNative -> Python.maxScalaNativeWarningMsg)
2425
)
2526
)
2627
Right(options)
@@ -29,4 +30,6 @@ final case class Python(
2930

3031
object Python {
3132
val handler: DirectiveHandler[Python] = DirectiveHandler.derive
33+
val maxScalaNativeWarningMsg =
34+
s"ScalaPy does not support Scala Native ${Constants.scalaNativeVersion}, ${Constants.scalaPyMaxScalaNative} should be used instead."
3235
}

modules/directives/src/main/scala/scala/build/preprocessing/directives/Toolkit.scala

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ object Toolkit {
3737
val typelevel = "typelevel"
3838
val scala = "scala"
3939

40+
def maxScalaNativeWarningMsg(toolkitName: String, maxNative: String): String =
41+
s"$toolkitName does not support Scala Native ${Constants.scalaNativeVersion}, $maxNative should be used instead."
42+
4043
object TypelevelToolkit {
4144
def unapply(s: Option[String]): Boolean =
4245
s.contains(typelevel) || s.contains(Constants.typelevelOrganization)
@@ -122,9 +125,19 @@ object Toolkit {
122125
ToolkitDefaults(isScalaToolkitDefault, isTypelevelToolkitDefault)
123126
) =>
124127
val scalaToolkitMaxNativeVersions =
125-
if isScalaToolkitDefault then List(Constants.toolkitMaxScalaNative) else Nil
128+
if isScalaToolkitDefault then
129+
List(Constants.toolkitMaxScalaNative -> maxScalaNativeWarningMsg(
130+
"Scala Toolkit",
131+
Constants.toolkitMaxScalaNative
132+
))
133+
else Nil
126134
val typelevelToolkitMaxNativeVersions =
127-
if isTypelevelToolkitDefault then List(Constants.typelevelToolkitMaxScalaNative) else Nil
135+
if isTypelevelToolkitDefault then
136+
List(Constants.typelevelToolkitMaxScalaNative -> maxScalaNativeWarningMsg(
137+
"TypeLevel Toolkit",
138+
Constants.typelevelToolkitMaxScalaNative
139+
))
140+
else Nil
128141
val maxNativeVersions =
129142
(scalaToolkitMaxNativeVersions ++ typelevelToolkitMaxNativeVersions).distinct
130143
positionedDep

modules/integration/src/test/scala/scala/cli/integration/RunScalaNativeTestDefinitions.scala

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,20 @@ trait RunScalaNativeTestDefinitions { _: RunTestDefinitions =>
323323
|""".stripMargin
324324
).fromRoot { root =>
325325
val result = os.proc(TestUtil.cli, "run", "toolkit.scala", cmdLineOpts, extraOptions)
326-
.call(cwd = root)
326+
.call(cwd = root, stderr = os.Pipe)
327327
expect(result.out.trim() == expectedMessage)
328+
if (Constants.scalaNativeVersion != Constants.typelevelToolkitMaxScalaNative) {
329+
val err = result.err.trim()
330+
expect(
331+
err.contains(
332+
s"Scala Native default version ${Constants.scalaNativeVersion} is not supported in this build"
333+
)
334+
)
335+
expect(err.contains(s"Using ${Constants.typelevelToolkitMaxScalaNative} instead."))
336+
expect(err.contains(
337+
s"TypeLevel Toolkit does not support Scala Native ${Constants.scalaNativeVersion}"
338+
))
339+
}
328340
}
329341
}
330342

@@ -347,8 +359,20 @@ trait RunScalaNativeTestDefinitions { _: RunTestDefinitions =>
347359
|""".stripMargin
348360
).fromRoot { root =>
349361
val result = os.proc(TestUtil.cli, "run", "toolkit.scala", cmdLineOpts, extraOptions)
350-
.call(cwd = root)
362+
.call(cwd = root, stderr = os.Pipe)
351363
expect(result.out.trim() == root.toString)
364+
if (Constants.scalaNativeVersion != Constants.toolkiMaxScalaNative) {
365+
val err = result.err.trim()
366+
expect(
367+
err.contains(
368+
s"Scala Native default version ${Constants.scalaNativeVersion} is not supported in this build"
369+
)
370+
)
371+
expect(err.contains(s"Using ${Constants.toolkiMaxScalaNative} instead."))
372+
expect(err.contains(
373+
s"Scala Toolkit does not support Scala Native ${Constants.scalaNativeVersion}"
374+
))
375+
}
352376
}
353377
}
354378
}

modules/integration/src/test/scala/scala/cli/integration/RunScalaPyTestDefinitions.scala

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,8 @@ trait RunScalaPyTestDefinitions { _: RunTestDefinitions =>
7070

7171
inputs.fromRoot { root =>
7272
val res =
73-
os.proc(TestUtil.cli, "--power", "run", extraOptions, ".", maybeCliArg).call(
74-
cwd = root
75-
)
73+
os.proc(TestUtil.cli, "--power", "run", extraOptions, ".", maybeCliArg)
74+
.call(cwd = root, stderr = os.Pipe)
7675
val output = res.out.trim()
7776
.linesIterator
7877
.filter { l =>
@@ -82,6 +81,18 @@ trait RunScalaPyTestDefinitions { _: RunTestDefinitions =>
8281
.mkString(System.lineSeparator())
8382
val expectedOutput = "Length is 3"
8483
expect(output == expectedOutput)
84+
val err = res.err.trim()
85+
if (Constants.scalaNativeVersion != Constants.scalaPyMaxScalaNative) {
86+
expect(
87+
err.contains(
88+
s"Scala Native default version ${Constants.scalaNativeVersion} is not supported in this build"
89+
)
90+
)
91+
expect(err.contains(s"Using ${Constants.scalaPyMaxScalaNative} instead."))
92+
expect(
93+
err.contains(s"ScalaPy does not support Scala Native ${Constants.scalaNativeVersion}")
94+
)
95+
}
8596
}
8697
}
8798

modules/options/src/main/scala/scala/build/options/BuildOptions.scala

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,19 @@ final case class BuildOptions(
418418
Some(notForBloopOptions.scalaJsLinkerOptions.finalScalaJsCliVersion)
419419
else None,
420420
scalaNativeCliVersion =
421-
if (platform.value == Platform.Native) Some(scalaNativeOptions.finalVersion) else None,
421+
if (platform.value == Platform.Native) {
422+
val scalaNativeFinalVersion = scalaNativeOptions.finalVersion
423+
if scalaNativeOptions.version.isEmpty && scalaNativeFinalVersion != Constants.scalaNativeVersion
424+
then
425+
scalaNativeOptions.maxDefaultNativeVersions.map(_._2).distinct
426+
.map(reason => s"[${Console.YELLOW}warn${Console.RESET}] $reason")
427+
.foreach(reason => logger.message(reason))
428+
logger.message(
429+
s"[${Console.YELLOW}warn${Console.RESET}] Scala Native default version ${Constants.scalaNativeVersion} is not supported in this build. Using $scalaNativeFinalVersion instead."
430+
)
431+
Some(scalaNativeFinalVersion)
432+
}
433+
else None,
422434
addScalapy =
423435
if (notForBloopOptions.doSetupPython.getOrElse(false))
424436
Some(notForBloopOptions.scalaPyVersion.getOrElse(Constants.scalaPyVersion))

modules/options/src/main/scala/scala/build/options/HasHashData.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ object HasHashData:
4646
): HasHashData[List[T]] = // shouldn't we hash index here?
4747
(name, list, update) => list.foreach(t => update(s"$name+=${hasher.hashedValue(t)}"))
4848

49+
given listOfTuple[T](using
50+
hasher: HashedType[T]
51+
): HasHashData[List[(T, T)]] =
52+
(name, list, update) =>
53+
list.foreach((t1, t2) => update(s"$name+=${hasher.hashedValue(t1) + hasher.hashedValue(t2)}"))
54+
4955
given option[T](using hasher: HashedType[T]): HasHashData[Option[T]] =
5056
(name, opt, update) => opt.foreach(t => update(s"$name=${hasher.hashedValue(t)}"))
5157

modules/options/src/main/scala/scala/build/options/ScalaNativeOptions.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,12 @@ final case class ScalaNativeOptions(
4040
compileDefaults: Option[Boolean] = None,
4141
embedResources: Option[Boolean] = None,
4242
buildTargetStr: Option[String] = None,
43-
maxDefaultNativeVersions: List[String] = Nil
43+
maxDefaultNativeVersions: List[(String, String)] = Nil
4444
) {
4545

4646
def defaultMaxVersion: Option[String] =
4747
maxDefaultNativeVersions
48+
.map(_._1)
4849
.map(v => v -> SNNumeralVersion.parse(v))
4950
.minByOption(_._2)
5051
.map(_._1)

0 commit comments

Comments
 (0)