diff --git a/modules/build/src/main/scala/scala/build/ScopedSources.scala b/modules/build/src/main/scala/scala/build/ScopedSources.scala index f77bc1baf1..a401a69c09 100644 --- a/modules/build/src/main/scala/scala/build/ScopedSources.scala +++ b/modules/build/src/main/scala/scala/build/ScopedSources.scala @@ -90,7 +90,7 @@ final case class ScopedSources( Sources.InMemory( Left("build-info"), os.rel / "BuildInfo.scala", - value(buildInfo(combinedOptions, workspace)).generateContents().getBytes( + value(buildInfo(combinedOptions, workspace, logger)).generateContents().getBytes( StandardCharsets.UTF_8 ), None @@ -123,7 +123,11 @@ final case class ScopedSources( buildOptionsFor(scope) .foldRight(baseOptions)(_.orElse(_)) - def buildInfo(baseOptions: BuildOptions, workspace: os.Path): Either[BuildException, BuildInfo] = + def buildInfo( + baseOptions: BuildOptions, + workspace: os.Path, + logger: Logger + ): Either[BuildException, BuildInfo] = either { def getScopedBuildInfo(scope: Scope): ScopedBuildInfo = val combinedOptions = combinedBuildOptions(scope, baseOptions) @@ -133,7 +137,7 @@ final case class ScopedSources( unwrappedScripts.flatMap(_.valueFor(scope).toSeq).flatMap(_.originalPath.toOption)) .map(_._2.toString) - ScopedBuildInfo(combinedOptions, sourcePaths ++ inMemoryPaths) + ScopedBuildInfo.forScope(combinedOptions, sourcePaths ++ inMemoryPaths, scope, logger) val baseBuildInfo = value(BuildInfo(combinedBuildOptions(Scope.Main, baseOptions), workspace)) diff --git a/modules/cli/src/main/scala/scala/cli/ScalaCliCommands.scala b/modules/cli/src/main/scala/scala/cli/ScalaCliCommands.scala index 8e9fd968f7..023ce995dd 100644 --- a/modules/cli/src/main/scala/scala/cli/ScalaCliCommands.scala +++ b/modules/cli/src/main/scala/scala/cli/ScalaCliCommands.scala @@ -42,6 +42,7 @@ class ScalaCliCommands( new HelpCmd(help), installcompletions.InstallCompletions, installhome.InstallHome, + listtargets.ListTargets, `new`.New, repl.Repl, package0.Package, diff --git a/modules/cli/src/main/scala/scala/cli/commands/listtargets/ListTargets.scala b/modules/cli/src/main/scala/scala/cli/commands/listtargets/ListTargets.scala new file mode 100644 index 0000000000..0612d81b2b --- /dev/null +++ b/modules/cli/src/main/scala/scala/cli/commands/listtargets/ListTargets.scala @@ -0,0 +1,73 @@ +package scala.cli.commands.listtargets + +import caseapp.* +import com.github.plokhotnyuk.jsoniter_scala.core.{JsonValueCodec, WriterConfig, writeToStream} +import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker + +import scala.build.* +import scala.build.errors.BuildException +import scala.build.input.Inputs +import scala.build.options.BuildOptions +import scala.cli.CurrentParams +import scala.cli.commands.shared.SharedOptions +import scala.cli.commands.{ScalaCommand, SpecificationLevel} + +object ListTargets extends ScalaCommand[ListTargetsOptions] { + override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.EXPERIMENTAL + override def names: List[List[String]] = List( + List("list-targets") + ) + override def sharedOptions(options: ListTargetsOptions): Option[SharedOptions] = + Some(options.shared) + + private final case class TargetEntry( + platform: String, + scalaVersion: Option[String] + ) + + private given JsonValueCodec[List[TargetEntry]] = JsonCodecMaker.make + + private def loadCrossSources( + inputs: Inputs, + buildOptions: BuildOptions, + logger: Logger + ): Either[BuildException, CrossSources] = + CrossSources.forInputs( + inputs, + Sources.defaultPreprocessors( + buildOptions.archiveCache, + buildOptions.internal.javaClassNameVersionOpt, + () => buildOptions.javaHome().value.javaCommand + ), + logger, + buildOptions.suppressWarningOptions, + buildOptions.internal.exclude, + download = buildOptions.downloader + ).map(_._1) + + private def targetOf(options: BuildOptions): TargetEntry = { + val platform = options.platform.value.repr + val sv = options.scalaParams.toOption.flatten.map(_.scalaVersion) + .orElse(options.scalaOptions.scalaVersion.flatMap(_.versionOpt)) + .orElse(options.scalaOptions.defaultScalaVersion) + TargetEntry(platform, sv) + } + + override def runCommand( + options: ListTargetsOptions, + args: RemainingArgs, + logger: Logger + ): Unit = { + val initialBuildOptions = buildOptionsOrExit(options) + val inputs = options.shared.inputs(args.all).orExit(logger) + CurrentParams.workspaceOpt = Some(inputs.workspace) + + val crossSources = loadCrossSources(inputs, initialBuildOptions, logger).orExit(logger) + val resolvedOptions = crossSources.sharedOptions(initialBuildOptions) + + val targets = (resolvedOptions +: resolvedOptions.crossOptions).map(targetOf).distinct.toList + + writeToStream(targets, System.out, WriterConfig.withIndentionStep(1)) + System.out.println() + } +} diff --git a/modules/cli/src/main/scala/scala/cli/commands/listtargets/ListTargetsOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/listtargets/ListTargetsOptions.scala new file mode 100644 index 0000000000..0970038c02 --- /dev/null +++ b/modules/cli/src/main/scala/scala/cli/commands/listtargets/ListTargetsOptions.scala @@ -0,0 +1,29 @@ +package scala.cli.commands.listtargets + +import caseapp.* + +import scala.cli.commands.shared.{HasSharedOptions, SharedOptions} + +@HelpMessage(ListTargetsOptions.helpMessage, "", ListTargetsOptions.detailedHelpMessage) +final case class ListTargetsOptions( + @Recurse + shared: SharedOptions = SharedOptions() +) extends HasSharedOptions + +object ListTargetsOptions { + implicit lazy val parser: Parser[ListTargetsOptions] = Parser.derive + implicit lazy val help: Help[ListTargetsOptions] = Help.derive + + private val helpHeader = + "Print the full matrix of declared build targets (platform x Scala version) as JSON." + val helpMessage: String = helpHeader + val detailedHelpMessage: String = + s"""$helpHeader + | + |Reads `using` directives and CLI options from the inputs and emits one entry per + |declared target, so external tools can enumerate the matrix without parsing + |directives themselves. + | + |Each entry has shape `{ "platform": "JVM"|"JS"|"Native", "scalaVersion": "..." }`. + |The `scalaVersion` field is omitted for pure-Java projects.""".stripMargin +} diff --git a/modules/cli/src/main/scala/scala/cli/exportCmd/JsonProject.scala b/modules/cli/src/main/scala/scala/cli/exportCmd/JsonProject.scala index c63bff1c77..3e34de5bed 100644 --- a/modules/cli/src/main/scala/scala/cli/exportCmd/JsonProject.scala +++ b/modules/cli/src/main/scala/scala/cli/exportCmd/JsonProject.scala @@ -5,13 +5,14 @@ import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker import java.io.PrintStream -import scala.build.info.{BuildInfo, ExportDependencyFormat, ScopedBuildInfo} +import scala.build.info.{BuildInfo, ExportDependencyFormat, NativeOptionsInfo, ScopedBuildInfo} import scala.util.Using final case class JsonProject(buildInfo: BuildInfo) extends Project { def sorted = this.copy( buildInfo = buildInfo.copy( - scopes = buildInfo.scopes.map { case (k, v) => k -> v.sorted } + scopes = buildInfo.scopes.map { case (k, v) => k -> v.sorted }, + nativeOptions = buildInfo.nativeOptions.map(_.sorted) ) ) @@ -49,6 +50,12 @@ final case class JsonProject(buildInfo: BuildInfo) extends Project { } } +extension (n: NativeOptionsInfo) { + def sorted(using ord: Ordering[String]) = n.copy( + toolingDependencies = n.toolingDependencies.sorted(using JsonProject.ordering) + ) +} + extension (s: ScopedBuildInfo) { def sorted(using ord: Ordering[String]) = s.copy( s.sources.sorted, @@ -56,6 +63,7 @@ extension (s: ScopedBuildInfo) { s.scalaCompilerPlugins.sorted(using JsonProject.ordering), s.dependencies.sorted(using JsonProject.ordering), s.compileOnlyDependencies.sorted(using JsonProject.ordering), + s.injectedDependencies.sorted(using JsonProject.ordering), s.resolvers.sorted, s.resourceDirs.sorted, s.customJarsDecls.sorted diff --git a/modules/cli/src/main/scala/scala/cli/exportCmd/JsonProjectDescriptor.scala b/modules/cli/src/main/scala/scala/cli/exportCmd/JsonProjectDescriptor.scala index 378f4a2c17..a0bd5b3bbc 100644 --- a/modules/cli/src/main/scala/scala/cli/exportCmd/JsonProjectDescriptor.scala +++ b/modules/cli/src/main/scala/scala/cli/exportCmd/JsonProjectDescriptor.scala @@ -16,16 +16,22 @@ final case class JsonProjectDescriptor( sourcesMain: Sources, sourcesTest: Sources ): Either[BuildException, JsonProject] = { - def getScopedBuildInfo(options: BuildOptions, sources: Sources) = + def getScopedBuildInfo(options: BuildOptions, sources: Sources, scope: Scope) = val sourcePaths = sources.paths.map(_._1.toString) val inMemoryPaths = sources.inMemory.flatMap(_.originalPath.toSeq.map(_._2.toString)) - ScopedBuildInfo(options, sourcePaths ++ inMemoryPaths) + ScopedBuildInfo.forScope( + options, + sourcePaths ++ inMemoryPaths, + scope, + logger, + injectTestRunner = true + ) for { baseBuildInfo <- BuildInfo(optionsMain, workspace) - mainBuildInfo = getScopedBuildInfo(optionsMain, sourcesMain) - testBuildInfo = getScopedBuildInfo(optionsTest, sourcesTest) + mainBuildInfo = getScopedBuildInfo(optionsMain, sourcesMain, Scope.Main) + testBuildInfo = getScopedBuildInfo(optionsTest, sourcesTest, Scope.Test) } yield JsonProject(baseBuildInfo .withScope(Scope.Main.name, mainBuildInfo) .withScope(Scope.Test.name, testBuildInfo)) diff --git a/modules/integration/src/test/scala/scala/cli/integration/ExportJsonTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/ExportJsonTestDefinitions.scala index 3ff2499767..92ea5f9661 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/ExportJsonTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/ExportJsonTestDefinitions.scala @@ -107,11 +107,22 @@ abstract class ExportJsonTestDefinitions extends ScalaCliSuite with TestScalaVer val jsonContents = readJson(exportJsonProc.out.text()) + val nativeVersion = Constants.scalaNativeVersion val expectedJsonContents = s"""{ |"scalaVersion":"3.2.2", |"platform":"Native", - |"scalaNativeVersion":"${Constants.scalaNativeVersion}", + |"scalaNativeVersion":"$nativeVersion", + |"nativeOptions": { + | "scalaNativeVersion":"$nativeVersion", + | "toolingDependencies": [ + | { + | "groupId":"org.scala-native", + | "artifactId":{"name":"scala-native-cli","fullName":"scala-native-cli_2.12"}, + | "version":"$nativeVersion" + | } + | ] + |}, |"scopes": { | "main": { | "sources": ["${withEscapedBackslashes(root / "Main.scala")}"], @@ -136,6 +147,23 @@ abstract class ExportJsonTestDefinitions extends ScalaCliSuite with TestScalaVer | "version":"0.7.8" | } | ], + | "injectedDependencies": [ + | { + | "groupId":"org.scala-native", + | "artifactId":{"name":"javalib_native0.5","fullName":"javalib_native0.5_3"}, + | "version":"$nativeVersion" + | }, + | { + | "groupId":"org.scala-native", + | "artifactId":{"name":"nscplugin","fullName":"nscplugin_3.2.2"}, + | "version":"$nativeVersion" + | }, + | { + | "groupId":"org.scala-native", + | "artifactId":{"name":"scala3lib_native0.5","fullName":"scala3lib_native0.5_3"}, + | "version":"3.2.2+$nativeVersion" + | } + | ], | "resolvers": [ | "https://repo1.maven.org/maven2", | "ivy:file:.../local-repo/...", @@ -165,6 +193,28 @@ abstract class ExportJsonTestDefinitions extends ScalaCliSuite with TestScalaVer | "version": "0.7.8" | } | ], + | "injectedDependencies": [ + | { + | "groupId":"org.scala-native", + | "artifactId":{"name":"javalib_native0.5","fullName":"javalib_native0.5_3"}, + | "version":"$nativeVersion" + | }, + | { + | "groupId":"org.scala-native", + | "artifactId":{"name":"nscplugin","fullName":"nscplugin_3.2.2"}, + | "version":"$nativeVersion" + | }, + | { + | "groupId":"org.scala-native", + | "artifactId":{"name":"scala3lib_native0.5","fullName":"scala3lib_native0.5_3"}, + | "version":"3.2.2+$nativeVersion" + | }, + | { + | "groupId":"org.scala-native", + | "artifactId":{"name":"test-interface_native0.5","fullName":"test-interface_native0.5_3"}, + | "version":"$nativeVersion" + | } + | ], | "resolvers": [ | "https://oss.sonatype.org/content/repositories/snapshots", | "https://repo1.maven.org/maven2", @@ -182,6 +232,168 @@ abstract class ExportJsonTestDefinitions extends ScalaCliSuite with TestScalaVer } } + test("export json injects JVM test-runner into test scope") { + val inputs = TestInputs( + os.rel / "Main.scala" -> + """object Main { + | def main(args: Array[String]): Unit = println("hi") + |} + |""".stripMargin, + os.rel / "unit.test.scala" -> + s"""//> using dep org.scalameta::munit::${Constants.munitVersion} + | + |class MyTest extends munit.FunSuite { test("ok") { assert(true) } } + |""".stripMargin + ) + inputs.fromRoot { root => + val exportJsonProc = + os.proc(TestUtil.cli, "--power", "export", "--json", ".", "--jvm", "temurin:17") + .call(cwd = root) + val jsonContents = readJson(exportJsonProc.out.text()) + val expectedFullName = s"test-runner_${Constants.scala3NextPrefix.split('.').head}" + // The test scope should include both munit and the scala-cli test-runner. + expect(jsonContents.contains("\"name\":\"test-runner\"")) + expect(jsonContents.contains(s"\"fullName\":\"$expectedFullName\"")) + expect(jsonContents.contains("\"groupId\":\"org.virtuslab.scala-cli\"")) + expect(jsonContents.contains("\"name\":\"munit\"")) + } + } + + test("export json includes JVM test-runner even when no test framework dep is declared") { + val inputs = TestInputs( + os.rel / "Main.scala" -> + """object Main { + | def main(args: Array[String]): Unit = println("hi") + |} + |""".stripMargin, + os.rel / "unit.test.scala" -> + """class MyTest { def foo() = () } + |""".stripMargin + ) + inputs.fromRoot { root => + val exportJsonProc = + os.proc(TestUtil.cli, "--power", "export", "--json", ".", "--jvm", "temurin:17") + .call(cwd = root) + val jsonContents = readJson(exportJsonProc.out.text()) + expect(jsonContents.contains("\"name\":\"test-runner\"")) + expect(jsonContents.contains("\"groupId\":\"org.virtuslab.scala-cli\"")) + } + } + + test("export json includes legacy JVM test-runner for Scala 2.12") { + val inputs = TestInputs( + os.rel / "Main.scala" -> + """object Main { + | def main(args: Array[String]): Unit = println("hi") + |} + |""".stripMargin, + os.rel / "unit.test.scala" -> + """class MyTest { def foo() = () } + |""".stripMargin + ) + inputs.fromRoot { root => + val exportJsonProc = + os.proc( + TestUtil.cli, + "--power", + "export", + "--json", + ".", + "--jvm", + "temurin:17", + "--scala", + Constants.scala212 + ) + .call(cwd = root) + val jsonContents = readJson(exportJsonProc.out.text()) + expect(jsonContents.contains("\"fullName\":\"test-runner_2.12\"")) + expect(jsonContents.contains("\"groupId\":\"org.virtuslab.scala-cli\"")) + expect(jsonContents.contains(s"\"version\":\"${Constants.runnerScala2LegacyVersion}\"")) + } + } + + test("export json includes legacy JVM test-runner for Scala 2.13") { + val inputs = TestInputs( + os.rel / "Main.scala" -> + """object Main { + | def main(args: Array[String]): Unit = println("hi") + |} + |""".stripMargin, + os.rel / "unit.test.scala" -> + """class MyTest { def foo() = () } + |""".stripMargin + ) + inputs.fromRoot { root => + val exportJsonProc = + os.proc( + TestUtil.cli, + "--power", + "export", + "--json", + ".", + "--jvm", + "temurin:17", + "--scala", + Constants.scala213 + ) + .call(cwd = root) + val jsonContents = readJson(exportJsonProc.out.text()) + expect(jsonContents.contains("\"fullName\":\"test-runner_2.13\"")) + expect(jsonContents.contains("\"groupId\":\"org.virtuslab.scala-cli\"")) + expect(jsonContents.contains(s"\"version\":\"${Constants.runnerScala2LegacyVersion}\"")) + } + } + + test("export json does not inject JVM test-runner for Native target") { + val inputs = TestInputs( + os.rel / "Main.scala" -> + """object Main { + | def main(args: Array[String]): Unit = println("hi") + |} + |""".stripMargin, + os.rel / "unit.test.scala" -> + """class MyTest { def foo() = () } + |""".stripMargin + ) + inputs.fromRoot { root => + val exportJsonProc = + os.proc(TestUtil.cli, "--power", "export", "--json", ".", "--native") + .call(cwd = root) + val jsonContents = readJson(exportJsonProc.out.text()) + // The JVM test-runner is JVM-only; Native targets get a Scala Native + // test-interface dep instead (verified by a separate test below). + expect(!jsonContents.contains("\"name\":\"test-runner\"")) + expect(!jsonContents.contains("org.virtuslab.scala-cli")) + } + } + + test("export json injects Scala Native test-interface into test scope of Native target") { + val inputs = TestInputs( + os.rel / "Main.scala" -> + """object Main { + | def main(args: Array[String]): Unit = println("hi") + |} + |""".stripMargin, + os.rel / "unit.test.scala" -> + """class MyTest { def foo() = () } + |""".stripMargin + ) + inputs.fromRoot { root => + val exportJsonProc = + os.proc(TestUtil.cli, "--power", "export", "--json", ".", "--native") + .call(cwd = root) + val jsonContents = readJson(exportJsonProc.out.text()) + val snBinary = Constants.scalaNativeVersion.split('.').take(2).mkString(".") + val expectedFullName = + s"test-interface_native${snBinary}_${Constants.scala3NextPrefix.split('.').head}" + // The test scope's injectedDependencies should include the Scala Native test-interface + // module pinned at scala-cli's bundled Scala Native version. + expect(jsonContents.contains("\"name\":\"test-interface_native" + snBinary + "\"")) + expect(jsonContents.contains(s"\"fullName\":\"$expectedFullName\"")) + expect(jsonContents.contains(s"\"version\":\"${Constants.scalaNativeVersion}\"")) + } + } + test("export json with js") { val inputs = TestInputs( os.rel / "Main.scala" -> @@ -245,6 +457,13 @@ abstract class ExportJsonTestDefinitions extends ScalaCliSuite with TestScalaVer | "version": "0.7.8" | } | ], + | "injectedDependencies": [ + | { + | "groupId":"org.scala-js", + | "artifactId":{"name":"scalajs-library","fullName":"scalajs-library_2.13"}, + | "version":"${Constants.scalaJsVersion}" + | } + | ], | "resolvers": [ | "https://repo1.maven.org/maven2", | "ivy:file:.../local-repo/...", diff --git a/modules/integration/src/test/scala/scala/cli/integration/ListTargetsTests.scala b/modules/integration/src/test/scala/scala/cli/integration/ListTargetsTests.scala new file mode 100644 index 0000000000..5ff44136d6 --- /dev/null +++ b/modules/integration/src/test/scala/scala/cli/integration/ListTargetsTests.scala @@ -0,0 +1,56 @@ +package scala.cli.integration + +import com.eed3si9n.expecty.Expecty.expect + +class ListTargetsTests extends ScalaCliSuite { + + private def normalize(s: String): String = s.replaceAll("\\s", "") + + test("list-targets emits the full platform x scala-version matrix") { + val inputs = TestInputs( + os.rel / "Main.scala" -> + """//> using platforms jvm native + |//> using scala 3.6.4 3.5.0 + |@main def hello = println("hi") + |""".stripMargin + ) + + inputs.fromRoot { root => + val res = os.proc(TestUtil.cli, "--power", "list-targets", ".").call(cwd = root) + val out = normalize(res.out.text()) + + val expected = normalize( + """[ + | { "platform": "JVM", "scalaVersion": "3.6.4" }, + | { "platform": "Native", "scalaVersion": "3.6.4" }, + | { "platform": "JVM", "scalaVersion": "3.5.0" }, + | { "platform": "Native", "scalaVersion": "3.5.0" } + |]""".stripMargin + ) + + expect(out == expected) + } + } + + test("list-targets with no cross directives returns a single entry") { + val inputs = TestInputs( + os.rel / "Main.scala" -> + """//> using scala 3.6.4 + |@main def hello = println("hi") + |""".stripMargin + ) + + inputs.fromRoot { root => + val res = os.proc(TestUtil.cli, "--power", "list-targets", ".").call(cwd = root) + val out = normalize(res.out.text()) + + val expected = normalize( + """[ + | { "platform": "JVM", "scalaVersion": "3.6.4" } + |]""".stripMargin + ) + + expect(out == expected) + } + } +} diff --git a/modules/options/src/main/scala/scala/build/Artifacts.scala b/modules/options/src/main/scala/scala/build/Artifacts.scala index c3cfa4c8ea..c6dd3dcf8d 100644 --- a/modules/options/src/main/scala/scala/build/Artifacts.scala +++ b/modules/options/src/main/scala/scala/build/Artifacts.scala @@ -118,6 +118,79 @@ object Artifacts { addScalapy: Option[String] ) + /** Selects the test-runner module version that scala-cli would resolve at test time. + * + * Falls back to a legacy version when the Scala or Java version is no longer supported by the + * current test-runner module. Mirrors the logic used inside [[Artifacts.apply]] so the export + * stays faithful to what test-time resolution would produce. + */ + def jvmTestRunnerVersion( + scalaVersion: String, + jvmVersion: Int, + logger: Logger, + logLegacyWarnings: Boolean + ): String = { + val shouldUseLegacyJava8Runners = jvmVersion < Constants.scala38MinJavaVersion + val shouldUseLegacyScala3Runners = + scalaVersion.startsWith("3") && + scalaVersion.coursierVersion < s"$scala3LtsPrefix.0".coursierVersion + val shouldUseLegacyScala2Runners = scalaVersion.startsWith("2") + val shouldUseLegacyScalaRunners = shouldUseLegacyScala3Runners || shouldUseLegacyScala2Runners + val shouldUseLegacyRunners = shouldUseLegacyScalaRunners || shouldUseLegacyJava8Runners + + val runnerLegacyVersion = + if scalaVersion.startsWith("3") then runnerScala30LegacyVersion + else runnerScala2LegacyVersion + + if shouldUseLegacyRunners then { + if logLegacyWarnings then { + if shouldUseLegacyScalaRunners then + logger.message( + s"$warnPrefix Scala $scalaVersion is no longer supported by the test-runner module." + ) + if shouldUseLegacyJava8Runners then + logger.message( + s"$warnPrefix Java $jvmVersion is no longer supported by the test-runner module." + ) + logger.message( + s"$warnPrefix Defaulting to a legacy test-runner module version: $runnerLegacyVersion." + ) + if shouldUseLegacyScalaRunners then + logger.message( + s"$warnPrefix To use the latest test-runner, upgrade Scala to at least $scala3LtsPrefix." + ) + if shouldUseLegacyJava8Runners then + logger.message( + s"$warnPrefix To use the latest test-runner, upgrade Java to at least ${Constants.defaultJavaVersion}." + ) + } + runnerLegacyVersion + } + else testRunnerVersion + } + + /** The test-runner dependency rendered for [[scala.build.info.ExportDependencyFormat]] consumers + * (e.g. `scala-cli export --json`). + * + * The artifact name is `test-runner_` to match how scala-cli resolves it at + * test time. + */ + def jvmTestRunnerExportDependency( + scalaVersion: String, + scalaBinaryVersion: String, + jvmVersion: Int, + logger: Logger + ): scala.build.info.ExportDependencyFormat = { + import scala.build.info.{ArtifactId, ExportDependencyFormat} + val version = jvmTestRunnerVersion(scalaVersion, jvmVersion, logger, logLegacyWarnings = false) + val fullName = s"$testRunnerModuleName${"_"}$scalaBinaryVersion" + ExportDependencyFormat( + groupId = testRunnerOrganization, + artifactId = ArtifactId(testRunnerModuleName, fullName), + version = version + ) + } + def apply( scalaArtifactsParamsOpt: Option[ScalaArtifactsParams], javacPluginDependencies: Seq[Positioned[AnyDependency]], @@ -159,34 +232,8 @@ object Artifacts { val jvmTestRunnerDependencies = if addJvmTestRunner then { - val runnerLegacyVersion = - if scalaVersion.startsWith("3") - then runnerScala30LegacyVersion - else runnerScala2LegacyVersion val testRunnerVersion0 = - if shouldUseLegacyRunners then { - if shouldUseLegacyScalaRunners then - logger.message( - s"$warnPrefix Scala $scalaVersion is no longer supported by the test-runner module." - ) - if shouldUseLegacyJava8Runners then - logger.message( - s"$warnPrefix Java $jvmVersion is no longer supported by the test-runner module." - ) - logger.message( - s"$warnPrefix Defaulting to a legacy test-runner module version: $runnerLegacyVersion." - ) - if shouldUseLegacyScalaRunners then - logger.message( - s"$warnPrefix To use the latest test-runner, upgrade Scala to at least $scala3LtsPrefix." - ) - if shouldUseLegacyJava8Runners then - logger.message( - s"$warnPrefix To use the latest test-runner, upgrade Java to at least ${Constants.defaultJavaVersion}." - ) - runnerLegacyVersion - } - else testRunnerVersion + jvmTestRunnerVersion(scalaVersion, jvmVersion, logger, logLegacyWarnings = true) Seq(dep"$testRunnerOrganization::$testRunnerModuleName:$testRunnerVersion0") } else Nil diff --git a/modules/options/src/main/scala/scala/build/info/BuildInfo.scala b/modules/options/src/main/scala/scala/build/info/BuildInfo.scala index 482621b751..c3fd1cbc86 100644 --- a/modules/options/src/main/scala/scala/build/info/BuildInfo.scala +++ b/modules/options/src/main/scala/scala/build/info/BuildInfo.scala @@ -6,6 +6,11 @@ import scala.build.info.BuildInfo.escapeBackslashes import scala.build.internal.Constants import scala.build.options.* +final case class NativeOptionsInfo( + scalaNativeVersion: String, + toolingDependencies: Seq[ExportDependencyFormat] = Nil +) + final case class BuildInfo( projectVersion: Option[String] = None, scalaVersion: Option[String] = None, @@ -14,6 +19,7 @@ final case class BuildInfo( scalaJsVersion: Option[String] = None, jsEsVersion: Option[String] = None, scalaNativeVersion: Option[String] = None, + nativeOptions: Option[NativeOptionsInfo] = None, mainClass: Option[String] = None, scopes: Map[String, ScopedBuildInfo] = Map.empty, scalaCliVersion: Option[String] = None @@ -152,11 +158,23 @@ object BuildInfo { ) } - private def scalaNativeSettings(options: ScalaNativeOptions): BuildInfo = + private def scalaNativeSettings(options: BuildOptions): BuildInfo = { + val nativeVersion = options.scalaNativeOptions.finalVersion + val toolingDeps = Seq(ExportDependencyFormat( + "org.scala-native", + ArtifactId("scala-native-cli", "scala-native-cli_2.12"), + nativeVersion + )) + BuildInfo( platform = Some(Platform.Native.repr), - scalaNativeVersion = Some(options.finalVersion) + scalaNativeVersion = Some(nativeVersion), + nativeOptions = Some(NativeOptionsInfo( + scalaNativeVersion = nativeVersion, + toolingDependencies = toolingDeps + )) ) + } private def jvmSettings(options: BuildOptions): BuildInfo = BuildInfo( @@ -173,7 +191,7 @@ object BuildInfo { case Some(Platform.JS) => scalaJsSettings(options.scalaJsOptions) case Some(Platform.Native) => - scalaNativeSettings(options.scalaNativeOptions) + scalaNativeSettings(options) case _ => jvmSettings(options) } diff --git a/modules/options/src/main/scala/scala/build/info/ScopedBuildInfo.scala b/modules/options/src/main/scala/scala/build/info/ScopedBuildInfo.scala index 391f6b3f61..90fb02383c 100644 --- a/modules/options/src/main/scala/scala/build/info/ScopedBuildInfo.scala +++ b/modules/options/src/main/scala/scala/build/info/ScopedBuildInfo.scala @@ -3,9 +3,10 @@ package scala.build.info import coursier.ivy.IvyRepository import coursier.maven.MavenRepository import coursier.{Dependency, LocalRepositories, Repositories} -import dependency.AnyDependency +import dependency.* -import scala.build.options.{BuildOptions, ConfigMonoid} +import scala.build.Logger +import scala.build.options.{BuildOptions, ConfigMonoid, Platform, Scope} final case class ScopedBuildInfo( sources: Seq[String] = Nil, @@ -13,6 +14,13 @@ final case class ScopedBuildInfo( scalaCompilerPlugins: Seq[ExportDependencyFormat] = Nil, dependencies: Seq[ExportDependencyFormat] = Nil, compileOnlyDependencies: Seq[ExportDependencyFormat] = Nil, + /** Dependencies scala-cli adds to this scope's Coursier resolution beyond what the user declared + * — e.g. the JVM test-runner, Scala Native runtime libs, the Native test-interface. Populated by + * `export --json` (so consumers like packaging tools see the same effective resolution input + * scala-cli would use at test/build time); empty in the generated runtime `BuildInfo` since the + * user didn't write these and scala-cli supplies them automatically. + */ + injectedDependencies: Seq[ExportDependencyFormat] = Nil, resolvers: Seq[String] = Nil, resourceDirs: Seq[String] = Nil, customJarsDecls: Seq[String] = Nil @@ -61,6 +69,157 @@ object ScopedBuildInfo { ) .reduceLeft(_ + _) + /** Build a [[ScopedBuildInfo]] for [[scope]]. When [[injectTestRunner]] is true, also inject the + * direct dependencies that scala-cli adds to this scope's Coursier resolution at `test` time, so + * consumers of `export --json` see the same effective resolution input scala-cli would use. The + * runtime `BuildInfo.scala` generator must leave it false — these deps are supplied by scala-cli + * at runtime, not declared by the user. + * + * scala-cli builds the main and test scopes as two separate `Build`s, each with its own Coursier + * resolution. This method models that: each scope's `dependencies` field is the effective + * direct-dep set for that scope's standalone resolution. When the two scopes pick conflicting + * transitive winners (e.g. a test framework pinning a higher Scala Native version than the main + * scope's `scala3lib_native`), each scope still gets its own winner — the export preserves that + * asymmetry by listing per-scope effective inputs. + * + * Injected items per scope (all gated on `injectTestRunner = true`, mirroring how + * `addTestRunnerDependency = true` on the shared `BuildOptions` flows into both scopes inside + * scala-cli's `test` command): + * - Native runtime deps (`javalib_native`, `scala3lib_native`/`scalalib_native`) and the + * `nscplugin` compiler plugin: both scopes, when platform is Native. + * - JS runtime deps (`scalajs-library`) and the Scala 2 `scalajs-compiler` plugin: both + * scopes, when platform is JS. + * - JVM test-runner (`org.virtuslab.scala-cli:test-runner_`): Test scope, when + * platform is JVM and the build has a Scala version. + * - Native test-interface (`org.scala-native:test-interface_native_`): + * Test scope, when platform is Native and Scala Native is at least 0.4.3. Coursier may still + * pick a higher transitive version as the test scope's winner, but the main scope's + * resolution pins scala-cli's bundled version, so an offline cache built from this export + * carries both. + * - JS test-bridge (`org.scala-js:scalajs-test-bridge`): Test scope, when platform is JS. + * + * Each injection is also gated on the relevant scope being non-empty (matching + * [[scala.build.Artifacts.apply]]'s gating). + */ + def forScope( + options: BuildOptions, + sourcePaths: Seq[String], + scope: Scope, + logger: Logger, + injectTestRunner: Boolean = false + ): ScopedBuildInfo = { + val base = apply(options, sourcePaths) + if injectTestRunner && sourcePaths.nonEmpty then + withInjectedDeps(base, options, scope, logger) + else base + } + + /** Inject the direct dependencies scala-cli adds to this scope's Coursier resolution at test + * time. See [[forScope]] for the per-platform/scope conditions. + */ + private def withInjectedDeps( + base: ScopedBuildInfo, + options: BuildOptions, + scope: Scope, + logger: Logger + ): ScopedBuildInfo = { + val scalaParamsOpt = options.scalaParams.toOption.flatten + if scalaParamsOpt.isEmpty then base + else { + val scalaParams = scalaParamsOpt.get + val platform = options.platform.value + val isTest = scope == Scope.Test + val platformDeps = platform match { + case Platform.Native => nativeScopeDeps(options, scalaParamsOpt) + case Platform.JS => jsScopeDeps(options, scalaParamsOpt) + case _ => Nil + } + val testRunnerDepOpt = (platform, isTest) match { + case (Platform.JVM, true) => + Some(scala.build.Artifacts.jvmTestRunnerExportDependency( + scalaVersion = scalaParams.scalaVersion, + scalaBinaryVersion = scalaParams.scalaBinaryVersion, + jvmVersion = options.javaHome().value.version, + logger = logger + )) + case (Platform.Native, true) => + nativeTestInterfaceExportDependency(options, scalaParamsOpt) + case (Platform.JS, true) => + Some(jsTestBridgeExportDependency(options)) + case _ => None + } + val injected = platformDeps ++ testRunnerDepOpt.toSeq + if injected.isEmpty then base + else base.copy(injectedDependencies = base.injectedDependencies ++ injected) + } + } + + /** Native runtime deps + compiler plugin that scala-cli adds to every Native scope's resolution + * (both Main and Test). These come from [[ScalaNativeOptions.nativeDependencies]] and + * [[ScalaNativeOptions.compilerPlugins]], which feed `BuildOptions.defaultDependencies`. + */ + private def nativeScopeDeps( + options: BuildOptions, + scalaParamsOpt: Option[dependency.ScalaParameters] + ): Seq[ExportDependencyFormat] = { + val nativeOptions = options.scalaNativeOptions + val sv = scalaParamsOpt.map(_.scalaVersion) + .getOrElse(scala.build.internal.Constants.defaultScalaVersion) + val runtime = nativeOptions.nativeDependencies(sv) + val compilerPlugins = nativeOptions.compilerPlugins + (runtime ++ compilerPlugins).map(ExportDependencyFormat(_, scalaParamsOpt)) + } + + /** JS runtime deps + compiler plugin (Scala 2 only) that scala-cli adds to every JS scope's + * resolution. Counterpart of [[nativeScopeDeps]] for the JS platform. + */ + private def jsScopeDeps( + options: BuildOptions, + scalaParamsOpt: Option[dependency.ScalaParameters] + ): Seq[ExportDependencyFormat] = { + val jsOptions = options.scalaJsOptions + val sv = scalaParamsOpt.map(_.scalaVersion) + .getOrElse(scala.build.internal.Constants.defaultScalaVersion) + val runtime = jsOptions.jsDependencies(sv) + val compilerPlugins = jsOptions.compilerPlugins(sv) + (runtime ++ compilerPlugins).map(ExportDependencyFormat(_, scalaParamsOpt)) + } + + /** Mirrors [[scala.build.options.BuildOptions.addNativeTestInterface]]: returns the + * `test-interface_native_:` dep that scala-cli's test-time + * resolution injects, when Scala Native is at least 0.4.3 (the version that started shipping a + * separate `test-interface` artifact). + */ + private def nativeTestInterfaceExportDependency( + options: BuildOptions, + scalaParamsOpt: Option[dependency.ScalaParameters] + ): Option[ExportDependencyFormat] = { + val snVersion = options.scalaNativeOptions.finalVersion + val minVer = coursier.core.Version("0.4.3") + if minVer.compareTo(coursier.core.Version(snVersion)) <= 0 then + Some(ExportDependencyFormat( + dep"org.scala-native::test-interface::$snVersion", + scalaParamsOpt + )) + else None + } + + /** Mirrors the JS test-bridge injection in [[scala.build.Artifacts.apply]]: the artifact id + * differs between Scala 2.x (cross-versioned with the project's Scala binary) and Scala 3 + * (always `scalajs-test-bridge_2.13`). + */ + private def jsTestBridgeExportDependency( + options: BuildOptions + ): ExportDependencyFormat = { + val scalaParams = options.scalaParams.toOption.flatten + val sv = scalaParams.map(_.scalaVersion).getOrElse("") + val jsVersion = options.scalaJsOptions.finalVersion + val dep0 = + if sv.startsWith("2.") then dep"org.scala-js::scalajs-test-bridge:$jsVersion" + else dep"org.scala-js:scalajs-test-bridge_2.13:$jsVersion" + ExportDependencyFormat(dep0, scalaParams) + } + private def scalacOptionsSettings(options: BuildOptions): ScopedBuildInfo = ScopedBuildInfo(scalacOptions = options.scalaOptions.scalacOptions.toSeq.map(_.value.value)) diff --git a/website/docs/reference/cli-options.md b/website/docs/reference/cli-options.md index 45f360181f..54730628ac 100644 --- a/website/docs/reference/cli-options.md +++ b/website/docs/reference/cli-options.md @@ -46,7 +46,7 @@ are assumed to be Scala compiler options and will be propagated to Scala Compile Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -62,7 +62,7 @@ Set JMH version (default: 1.37) Available in commands: -[`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall) +[`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall) @@ -219,7 +219,7 @@ Force overwriting values for key Available in commands: -[`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall) +[`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall) @@ -258,7 +258,7 @@ Run given command against all provided Scala versions and/or platforms Available in commands: -[`bloop`](./commands.md#bloop), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bloop`](./commands.md#bloop), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -278,7 +278,7 @@ Debug mode (attach by default) Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -511,7 +511,7 @@ Pass scalafmt version before running it (3.11.0 by default). If passed, this ove Available in commands: -[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version) +[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`list-targets`](./commands.md#list-targets), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version) @@ -531,7 +531,7 @@ Suppress warnings about using deprecated features Available in commands: -[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp create`](./commands.md#pgp-create), [`pgp key-id`](./commands.md#pgp-key-id), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`pgp sign`](./commands.md#pgp-sign), [`pgp verify`](./commands.md#pgp-verify), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version) +[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`list-targets`](./commands.md#list-targets), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp create`](./commands.md#pgp-create), [`pgp key-id`](./commands.md#pgp-key-id), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`pgp sign`](./commands.md#pgp-sign), [`pgp verify`](./commands.md#pgp-verify), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version) @@ -555,7 +555,7 @@ Print help message, including hidden options, and exit Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -661,7 +661,7 @@ Add java properties. Note that options equal `-Dproperty=value` are assumed to b Available in commands: -[`bloop`](./commands.md#bloop), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bloop`](./commands.md#bloop), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -711,7 +711,7 @@ Port for BSP debugging Available in commands: -[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version) +[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`list-targets`](./commands.md#list-targets), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version) @@ -749,7 +749,7 @@ List main classes available in the current context Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -1012,7 +1012,7 @@ Key server to push / pull keys from Available in commands: -[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version) +[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`list-targets`](./commands.md#list-targets), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version) @@ -1234,7 +1234,7 @@ Dummy mode - don't upload any secret to GitHub Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -1343,7 +1343,7 @@ Run Java commands using a manifest-based class path (shortens command length) Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -1442,7 +1442,7 @@ Whether to run the Scala.js CLI on the JVM or using a native executable Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -1516,7 +1516,7 @@ Enable/disable Scala Native multithreading support Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -1534,7 +1534,7 @@ Add a `scalac` option. Note that options starting with `-g`, `-language`, `-opt` Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -1574,7 +1574,7 @@ Run scalafix rule(s) explicitly, overriding the configuration file default. Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -1621,7 +1621,7 @@ Aliases: `-n` Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -1737,7 +1737,7 @@ Option with deprecated alias (internal, do not use) Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -1786,7 +1786,7 @@ A synonym to --markdown-snippet, which defaults the sub-command to `run` when no Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -1800,7 +1800,7 @@ Generate BuildInfo for project Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -1924,7 +1924,7 @@ A github token used to access GitHub. Not needed in most cases. Available in commands: -[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version) +[`add-path`](./commands.md#add-path), [`bloop`](./commands.md#bloop), [`bloop exit`](./commands.md#bloop-exit), [`bloop output`](./commands.md#bloop-output), [`bloop start`](./commands.md#bloop-start), [`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`config`](./commands.md#config), [`default-file`](./commands.md#default-file), [`dependency-update`](./commands.md#dependency-update), [`directories`](./commands.md#directories), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`help`](./commands.md#help), [`install completions` , `install-completions`](./commands.md#install-completions), [`install-home`](./commands.md#install-home), [`list-targets`](./commands.md#list-targets), [`new`](./commands.md#new), [`package`](./commands.md#package), [`pgp pull`](./commands.md#pgp-pull), [`pgp push`](./commands.md#pgp-push), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`github secret create` , `gh secret create`](./commands.md#github-secret-create), [`github secret list` , `gh secret list`](./commands.md#github-secret-list), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`uninstall`](./commands.md#uninstall), [`uninstall completions` , `uninstall-completions`](./commands.md#uninstall-completions), [`update`](./commands.md#update), [`version`](./commands.md#version) @@ -1948,7 +1948,7 @@ Enable actionable diagnostics Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`version`](./commands.md#version) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test), [`version`](./commands.md#version) @@ -2126,7 +2126,7 @@ Force overwriting destination files Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -2345,7 +2345,7 @@ Time to wait between staging repository operation retries, in milliseconds. Available in commands: -[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) @@ -2385,7 +2385,7 @@ Available in commands: Available in commands: -[`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) +[`bsp`](./commands.md#bsp), [`clean`](./commands.md#clean), [`compile`](./commands.md#compile), [`dependency-update`](./commands.md#dependency-update), [`doc`](./commands.md#doc), [`export`](./commands.md#export), [`fix`](./commands.md#fix), [`fmt` , `format` , `scalafmt`](./commands.md#fmt), [`list-targets`](./commands.md#list-targets), [`package`](./commands.md#package), [`publish`](./commands.md#publish), [`publish local`](./commands.md#publish-local), [`publish setup`](./commands.md#publish-setup), [`repl` , `console`](./commands.md#repl), [`run`](./commands.md#run), [`setup-ide`](./commands.md#setup-ide), [`shebang`](./commands.md#shebang), [`test`](./commands.md#test) diff --git a/website/docs/reference/commands.md b/website/docs/reference/commands.md index afb2b8c70b..dc3f242a5f 100644 --- a/website/docs/reference/commands.md +++ b/website/docs/reference/commands.md @@ -180,6 +180,23 @@ For detailed documentation refer to our website: https://scala-cli.virtuslab.org Accepts option groups: [global suppress warning](./cli-options.md#global-suppress-warning-options), [install completions](./cli-options.md#install-completions-options), [logging](./cli-options.md#logging-options), [power](./cli-options.md#power-options), [verbosity](./cli-options.md#verbosity-options) +## list-targets + +Print the full matrix of declared build targets (platform x Scala version) as JSON. + +Reads `using` directives and CLI options from the inputs and emits one entry per +declared target, so external tools can enumerate the matrix without parsing +directives themselves. + +Each entry has shape `{ "platform": "JVM"|"JS"|"Native", "scalaVersion": "..." }`. +The `scalaVersion` field is omitted for pure-Java projects. + +The `list-targets` sub-command is experimental. +Please bear in mind that non-ideal user experience should be expected. +If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli + +Accepts option groups: [benchmarking](./cli-options.md#benchmarking-options), [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [markdown](./cli-options.md#markdown-options), [power](./cli-options.md#power-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [scope](./cli-options.md#scope-options), [semantic db](./cli-options.md#semantic-db-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [workspace](./cli-options.md#workspace-options) + ## new New giter8 template.