diff --git a/build.sc b/build.sc index 12ebd87a15..3a6d663941 100644 --- a/build.sc +++ b/build.sc @@ -398,6 +398,7 @@ trait Core extends ScalaCliSbtModule with ScalaCliPublishModule with HasTests | | def toolkitOrganization = "${Deps.toolkit.dep.module.organization.value}" | def toolkitName = "${Deps.toolkit.dep.module.name.value}" + | def typelevelOrganization = "org.typelevel" | | def defaultScalaVersion = "${Scala.defaultUser}" | def defaultScala212Version = "${Scala.scala212}" diff --git a/modules/build/src/test/scala/scala/build/tests/DirectiveTests.scala b/modules/build/src/test/scala/scala/build/tests/DirectiveTests.scala index f9acf82e0f..5edb54e29e 100644 --- a/modules/build/src/test/scala/scala/build/tests/DirectiveTests.scala +++ b/modules/build/src/test/scala/scala/build/tests/DirectiveTests.scala @@ -107,4 +107,23 @@ class DirectiveTests extends munit.FunSuite { } } + test("resolve typelevel toolkit dependency") { + val testInputs = TestInputs( + os.rel / "simple.sc" -> + """//> using toolkit "typelevel:latest" + |""".stripMargin + ) + testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) { + (_, _, maybeBuild) => + val build = maybeBuild.orThrow + val dep = build.options.classPathOptions.extraDependencies.toSeq.headOption + assert(dep.nonEmpty) + + val toolkitDep = dep.get.value + expect(toolkitDep.organization == "org.typelevel") + expect(toolkitDep.name == "toolkit") + expect(toolkitDep.version == "latest.release") + } + } + } diff --git a/modules/cli/src/test/scala/cli/commands/tests/RunOptionsTests.scala b/modules/cli/src/test/scala/cli/commands/tests/RunOptionsTests.scala index ae7aad126a..3aa077bbcd 100644 --- a/modules/cli/src/test/scala/cli/commands/tests/RunOptionsTests.scala +++ b/modules/cli/src/test/scala/cli/commands/tests/RunOptionsTests.scala @@ -36,4 +36,20 @@ class RunOptionsTests extends munit.FunSuite { expect(toolkitDep.version == "latest.release") } + test("resolve typelevel toolkit dependency") { + val runOptions = RunOptions( + shared = SharedOptions( + withToolkit = Some("typelevel:latest") + ) + ) + val buildOptions = Run.buildOptions(runOptions).value + val dep = buildOptions.classPathOptions.extraDependencies.toSeq.headOption + assert(dep.nonEmpty) + + val toolkitDep = dep.get.value + expect(toolkitDep.organization == "org.typelevel") + expect(toolkitDep.name == "toolkit") + expect(toolkitDep.version == "latest.release") + } + } diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/Toolkit.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Toolkit.scala index 67122dd7e4..87298818ab 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/Toolkit.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/Toolkit.scala @@ -39,9 +39,17 @@ final case class Toolkit( } object Toolkit { - def resolveDependency(toolkitVersion: Positioned[String]) = toolkitVersion.map(version => - val v = if version == "latest" then "latest.release" else version - dep"${Constants.toolkitOrganization}::${Constants.toolkitName}::$v,toolkit" + def resolveDependency(toolkitCoord: Positioned[String]) = toolkitCoord.map(coord => + val tokens = coord.split(':') + val version = tokens.last + val v = if version == "latest" then "latest.release" else version + val flavor = tokens.dropRight(1).headOption + val org = flavor match { + case Some("typelevel") => Constants.typelevelOrganization + case Some(org) => org + case None => Constants.toolkitOrganization + } + dep"$org::${Constants.toolkitName}::$v,toolkit" ) val handler: DirectiveHandler[Toolkit] = DirectiveHandler.derive } diff --git a/modules/integration/src/test/scala/scala/cli/integration/DependencyUpdateTests.scala b/modules/integration/src/test/scala/scala/cli/integration/DependencyUpdateTests.scala index 74ba455b35..9841473672 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/DependencyUpdateTests.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/DependencyUpdateTests.scala @@ -60,4 +60,33 @@ class DependencyUpdateTests extends ScalaCliSuite { expect(Version(updatedToolkitVersion) > Version(toolkitVersion)) } } + + test("update typelevel toolkit dependence") { + val toolkitVersion = "0.0.1" + val testInputs = TestInputs( + os.rel / "Foo.scala" -> + s"""//> using toolkit "typelevel:$toolkitVersion" + | + |import cats.effect.* + | + |object Hello extends IOApp.Simple { + | def run = IO.println("Hello") + |} + |""".stripMargin + ) + testInputs.fromRoot { root => + // update toolkit + os.proc(TestUtil.cli, "--power", "dependency-update", "--all", ".") + .call(cwd = root) + + val toolkitDirective = "//> using toolkit \"(.*)\"".r + val updatedToolkitVersionOpt = { + val regexMatch = toolkitDirective.findFirstMatchIn(os.read(root / "Foo.scala")) + regexMatch.map(_.group(1)) + } + expect(updatedToolkitVersionOpt.nonEmpty) + val updatedToolkitVersion = updatedToolkitVersionOpt.get + expect(Version(updatedToolkitVersion) > Version(toolkitVersion)) + } + } } diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala index c8ee14194b..bdf5423e64 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala @@ -1129,6 +1129,26 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) } } + test("should add typelevel toolkit to classpath") { + val inputs = TestInputs( + os.rel / "Hello.scala" -> + s"""|import cats.effect.* + |import fs2.io.file.Files + |object Hello extends IOApp.Simple { + | // IO should be added to classpath by typelevel toolkit + | def run = Files[IO].currentWorkingDirectory.flatMap { cwd => + | IO.println(cwd.toString) + | } + |}""".stripMargin + ) + inputs.fromRoot { root => + val output = os.proc(TestUtil.cli, ".", "--toolkit", "typelevel:0.0.1") + .call(cwd = root).out.trim() + + expect(output == root.toString()) + } + } + test(s"print error if workspace path contains a ${File.pathSeparator}") { val msg = "Hello" val relPath = os.rel / s"weird${File.pathSeparator}directory" / "Hello.scala"