Skip to content

Commit 4097761

Browse files
committed
Ensure launcher options of setup-ide are respected by bsp
1 parent 7ad37cc commit 4097761

File tree

14 files changed

+189
-42
lines changed

14 files changed

+189
-42
lines changed

modules/cli/src/main/scala/scala/cli/ScalaCli.scala

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,11 @@ object ScalaCli {
5555
private def isGraalvmNativeImage: Boolean =
5656
sys.props.contains("org.graalvm.nativeimage.imagecode")
5757

58-
private var defaultScalaVersion: Option[String] = None
59-
val launcherPredefinedRepositories: ListBuffer[String] = ListBuffer.empty
58+
private var maybeLauncherOptions: Option[LauncherOptions] = None
6059

61-
def getDefaultScalaVersion: String = defaultScalaVersion.getOrElse(Constants.defaultScalaVersion)
60+
def launcherOptions: LauncherOptions = maybeLauncherOptions.getOrElse(LauncherOptions())
61+
def getDefaultScalaVersion: String =
62+
launcherOptions.cliUserScalaVersion.getOrElse(Constants.defaultScalaVersion)
6263

6364
private def partitionArgs(args: Array[String]): (Array[String], Array[String]) = {
6465
val systemProps = args.takeWhile(_.startsWith("-D"))
@@ -232,6 +233,7 @@ object ScalaCli {
232233
System.err.println(e.message)
233234
sys.exit(1)
234235
case Right((launcherOpts, args0)) =>
236+
maybeLauncherOptions = Some(launcherOpts)
235237
launcherOpts.cliVersion.map(_.trim).filter(_.nonEmpty) match {
236238
case Some(ver) =>
237239
val powerArgs =
@@ -247,10 +249,6 @@ object ScalaCli {
247249
launcherOpts.progName.foreach { pn =>
248250
progName = pn
249251
}
250-
if launcherOpts.cliUserScalaVersion.nonEmpty then
251-
defaultScalaVersion = launcherOpts.cliUserScalaVersion
252-
if launcherOpts.cliPredefinedRepository.nonEmpty then
253-
launcherPredefinedRepositories.addAll(launcherOpts.cliPredefinedRepository)
254252
if launcherOpts.powerOptions.power then
255253
isSipScala = false
256254
args0.toArray

modules/cli/src/main/scala/scala/cli/commands/ScalaCommand.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import caseapp.core.{Arg, Error, RemainingArgs}
99
import caseapp.{HelpMessage, Name}
1010
import coursier.core.{Repository, Version}
1111
import dependency.*
12+
import org.codehaus.plexus.classworlds.launcher.Launcher
1213

1314
import java.util.concurrent.atomic.{AtomicBoolean, AtomicReference}
1415

@@ -28,6 +29,7 @@ import scala.cli.commands.util.CommandHelpers
2829
import scala.cli.commands.util.ScalacOptionsUtil.*
2930
import scala.cli.config.{ConfigDb, Keys}
3031
import scala.cli.internal.ProcUtil
32+
import scala.cli.launcher.LauncherOptions
3133
import scala.cli.util.ConfigDbUtils.*
3234
import scala.cli.{CurrentParams, ScalaCli}
3335
import scala.util.{Properties, Try}
@@ -38,8 +40,9 @@ abstract class ScalaCommand[T <: HasGlobalOptions](implicit myParser: Parser[T],
3840
private val globalOptionsAtomic: AtomicReference[GlobalOptions] =
3941
new AtomicReference(GlobalOptions.default)
4042

41-
private def globalOptions: GlobalOptions = globalOptionsAtomic.get()
42-
protected def defaultScalaVersion: String = ScalaCli.getDefaultScalaVersion
43+
private def globalOptions: GlobalOptions = globalOptionsAtomic.get()
44+
protected def launcherOptions: LauncherOptions = ScalaCli.launcherOptions
45+
protected def defaultScalaVersion: String = ScalaCli.getDefaultScalaVersion
4346

4447
def sharedOptions(t: T): Option[SharedOptions] = // hello borked unused warning
4548
None

modules/cli/src/main/scala/scala/cli/commands/bsp/Bsp.scala

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import scala.cli.commands.ScalaCommand
1515
import scala.cli.commands.publish.ConfigUtil.*
1616
import scala.cli.commands.shared.SharedOptions
1717
import scala.cli.config.{ConfigDb, Keys}
18+
import scala.cli.launcher.LauncherOptions
1819
import scala.concurrent.Await
1920
import scala.concurrent.duration.Duration
2021

@@ -29,6 +30,14 @@ object Bsp extends ScalaCommand[BspOptions] {
2930
val content = os.read.bytes(os.Path(optionsPath, os.pwd))
3031
readFromArray(content)(SharedOptions.jsonCodec)
3132
}.getOrElse(options.shared)
33+
private def latestLauncherOptions(options: BspOptions): LauncherOptions =
34+
options.jsonLauncherOptions
35+
.map(path => os.Path(path, os.pwd))
36+
.filter(path => os.exists(path) && os.isFile(path))
37+
.map { optionsPath =>
38+
val content = os.read.bytes(os.Path(optionsPath, os.pwd))
39+
readFromArray(content)(LauncherOptions.jsonCodec)
40+
}.getOrElse(launcherOptions)
3241
private def latestEnvsFromFile(options: BspOptions): Map[String, String] =
3342
options.envs
3443
.map(path => os.Path(path, os.pwd))
@@ -48,19 +57,21 @@ object Bsp extends ScalaCommand[BspOptions] {
4857
pprint.err.log(args)
4958

5059
val getSharedOptions: () => SharedOptions = () => latestSharedOptions(options)
60+
val getLauncherOptions: () => LauncherOptions = () => latestLauncherOptions(options)
5161
val getEnvsFromFile: () => Map[String, String] = () => latestEnvsFromFile(options)
5262

5363
val preprocessInputs: Seq[String] => Either[BuildException, (Inputs, BuildOptions)] =
5464
argsSeq =>
5565
either {
56-
val sharedOptions = getSharedOptions()
57-
val envs = getEnvsFromFile()
58-
val initialInputs = value(sharedOptions.inputs(argsSeq, () => Inputs.default()))
66+
val sharedOptions = getSharedOptions()
67+
val launcherOptions = getLauncherOptions()
68+
val envs = getEnvsFromFile()
69+
val initialInputs = value(sharedOptions.inputs(argsSeq, () => Inputs.default()))
5970

6071
if (sharedOptions.logging.verbosity >= 3)
6172
pprint.err.log(initialInputs)
6273

63-
val baseOptions = buildOptions(sharedOptions, envs)
74+
val baseOptions = buildOptions(sharedOptions, launcherOptions, envs)
6475
val latestLogger = sharedOptions.logging.logger
6576
val persistentLogger = new PersistentDiagnosticLogger(latestLogger)
6677

@@ -99,8 +110,10 @@ object Bsp extends ScalaCommand[BspOptions] {
99110
*/
100111
val initialBspOptions = {
101112
val sharedOptions = getSharedOptions()
113+
val launcherOptions = getLauncherOptions()
102114
val envs = getEnvsFromFile()
103-
val bspBuildOptions = buildOptions(sharedOptions, envs).orElse(finalBuildOptions)
115+
val bspBuildOptions = buildOptions(sharedOptions, launcherOptions, envs)
116+
.orElse(finalBuildOptions)
104117
BspReloadableOptions(
105118
buildOptions = bspBuildOptions,
106119
bloopRifleConfig = sharedOptions.bloopRifleConfig(Some(bspBuildOptions))
@@ -111,13 +124,14 @@ object Bsp extends ScalaCommand[BspOptions] {
111124
}
112125

113126
val bspReloadableOptionsReference = BspReloadableOptions.Reference { () =>
114-
val sharedOptions = getSharedOptions()
115-
val envs = getEnvsFromFile()
127+
val sharedOptions = getSharedOptions()
128+
val launcherOptions = getLauncherOptions()
129+
val envs = getEnvsFromFile()
116130
val bloopRifleConfig = sharedOptions.bloopRifleConfig(Some(finalBuildOptions))
117131
.orExit(sharedOptions.logger)
118132

119133
BspReloadableOptions(
120-
buildOptions = buildOptions(sharedOptions, envs),
134+
buildOptions = buildOptions(sharedOptions, launcherOptions, envs),
121135
bloopRifleConfig = sharedOptions.bloopRifleConfig().orExit(sharedOptions.logger),
122136
logger = sharedOptions.logging.logger,
123137
verbosity = sharedOptions.logging.verbosity
@@ -148,11 +162,12 @@ object Bsp extends ScalaCommand[BspOptions] {
148162

149163
private def buildOptions(
150164
sharedOptions: SharedOptions,
165+
launcherOptions: LauncherOptions,
151166
envs: Map[String, String]
152167
): BuildOptions = {
153168
val logger = sharedOptions.logger
154169
val baseOptions = sharedOptions.buildOptions().orExit(logger)
155-
val adjustedOptions = baseOptions.copy(
170+
val withDefaults = baseOptions.copy(
156171
classPathOptions = baseOptions.classPathOptions.copy(
157172
fetchSources = baseOptions.classPathOptions.fetchSources.orElse(Some(true))
158173
),
@@ -167,18 +182,28 @@ object Bsp extends ScalaCommand[BspOptions] {
167182
baseOptions.notForBloopOptions.addRunnerDependencyOpt.orElse(Some(false))
168183
)
169184
)
170-
envs.get("JAVA_HOME")
171-
.filter(_ => adjustedOptions.javaOptions.javaHomeOpt.isEmpty)
185+
val withEnvs = envs.get("JAVA_HOME")
186+
.filter(_ => withDefaults.javaOptions.javaHomeOpt.isEmpty)
172187
.map(javaHome =>
173-
adjustedOptions.copy(javaOptions =
174-
adjustedOptions.javaOptions.copy(javaHomeOpt =
188+
withDefaults.copy(javaOptions =
189+
withDefaults.javaOptions.copy(javaHomeOpt =
175190
Some(Positioned(
176191
Seq(Position.Custom("ide.env.JAVA_HOME")),
177192
os.Path(javaHome, Os.pwd)
178193
))
179194
)
180195
)
181196
)
182-
.getOrElse(adjustedOptions)
197+
.getOrElse(withDefaults)
198+
val withLauncherOptions = withEnvs.copy(
199+
classPathOptions = withEnvs.classPathOptions.copy(
200+
extraRepositories =
201+
(withEnvs.classPathOptions.extraRepositories ++ launcherOptions.cliPredefinedRepository).distinct
202+
),
203+
scalaOptions = withEnvs.scalaOptions.copy(
204+
defaultScalaVersion = launcherOptions.cliUserScalaVersion
205+
)
206+
)
207+
withLauncherOptions
183208
}
184209
}

modules/cli/src/main/scala/scala/cli/commands/bsp/BspOptions.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ final case class BspOptions(
1919
@Tag(tags.implementation)
2020
jsonOptions: Option[String] = None,
2121

22+
@HelpMessage("Command-line launcher options JSON file")
23+
@ValueDescription("path")
24+
@Hidden
25+
@Tag(tags.implementation)
26+
jsonLauncherOptions: Option[String] = None,
27+
2228
@HelpMessage("Command-line options environment variables file")
2329
@ValueDescription("path")
2430
@Hidden

modules/cli/src/main/scala/scala/cli/commands/repl/Repl.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,18 @@ object Repl extends ScalaCommand[ReplOptions] {
6262
scalaOptions = baseOptions.scalaOptions.copy(
6363
scalaVersion = baseOptions.scalaOptions.scalaVersion
6464
.orElse {
65-
val defaultScalaVer = ScalaCli.getDefaultScalaVersion
6665
val shouldDowngrade = {
6766
def needsDowngradeForAmmonite = {
6867
import coursier.core.Version
69-
Version(maxAmmoniteScalaVer) < Version(defaultScalaVer)
68+
Version(maxAmmoniteScalaVer) < Version(defaultScalaVersion)
7069
}
7170
ammonite.contains(true) &&
7271
ammoniteVersionOpt.isEmpty &&
7372
needsDowngradeForAmmonite
7473
}
7574
if (shouldDowngrade) {
7675
logger.message(
77-
s"Scala $defaultScalaVer is not yet supported with this version of Ammonite"
76+
s"Scala $defaultScalaVersion is not yet supported with this version of Ammonite"
7877
)
7978
logger.message(s"Defaulting to Scala $maxAmmoniteScalaVer")
8079
Some(MaybeScalaVersion(maxAmmoniteScalaVer))

modules/cli/src/main/scala/scala/cli/commands/setupide/SetupIde.scala

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import scala.cli.CurrentParams
1919
import scala.cli.commands.shared.{SharedBspFileOptions, SharedOptions}
2020
import scala.cli.commands.{CommandUtils, ScalaCommand}
2121
import scala.cli.errors.FoundVirtualInputsError
22+
import scala.cli.launcher.LauncherOptions
2223
import scala.jdk.CollectionConverters.*
2324

2425
object SetupIde extends ScalaCommand[SetupIdeOptions] {
@@ -132,6 +133,8 @@ object SetupIde extends ScalaCommand[SetupIdeOptions] {
132133
val (bspName, bspJsonDestination) = bspDetails(inputs.workspace, options.bspFile)
133134
val scalaCliBspJsonDestination =
134135
inputs.workspace / Constants.workspaceDirName / "ide-options-v2.json"
136+
val scalaCliBspLauncherOptsJsonDestination =
137+
inputs.workspace / Constants.workspaceDirName / "ide-launcher-options.json"
135138
val scalaCliBspInputsJsonDestination =
136139
inputs.workspace / Constants.workspaceDirName / "ide-inputs.json"
137140
val scalaCliBspEnvsJsonDestination =
@@ -151,9 +154,12 @@ object SetupIde extends ScalaCommand[SetupIdeOptions] {
151154
)
152155

153156
val bspArgs =
154-
List(CommandUtils.getAbsolutePathToScalaCli(progName), "bsp") ++
157+
List(CommandUtils.getAbsolutePathToScalaCli(progName)) ++
158+
launcherOptions.toCliArgs ++
159+
List("bsp") ++
155160
debugOpt ++
156161
List("--json-options", scalaCliBspJsonDestination.toString) ++
162+
List("--json-launcher-options", scalaCliBspLauncherOptsJsonDestination.toString) ++
157163
List("--envs-file", scalaCliBspEnvsJsonDestination.toString) ++
158164
inputArgs
159165
val details = new BspConnectionDetails(
@@ -175,10 +181,11 @@ object SetupIde extends ScalaCommand[SetupIdeOptions] {
175181

176182
implicit val mapCodec: JsonValueCodec[Map[String, String]] = JsonCodecMaker.make
177183

178-
val json = gson.toJson(details)
179-
val scalaCliOptionsForBspJson = writeToArray(options.shared)(SharedOptions.jsonCodec)
180-
val scalaCliBspInputsJson = writeToArray(ideInputs)
181-
val scalaCliBspEnvsJson = writeToArray(sys.env)
184+
val json = gson.toJson(details)
185+
val scalaCliOptionsForBspJson = writeToArray(options.shared)(SharedOptions.jsonCodec)
186+
val scalaCliLaunchOptsForBspJson = writeToArray(launcherOptions)(LauncherOptions.jsonCodec)
187+
val scalaCliBspInputsJson = writeToArray(ideInputs)
188+
val scalaCliBspEnvsJson = writeToArray(sys.env)
182189

183190
if (inputs.workspaceOrigin.contains(WorkspaceOrigin.HomeDir))
184191
value(Left(new WorkspaceError(
@@ -194,6 +201,11 @@ object SetupIde extends ScalaCommand[SetupIdeOptions] {
194201
scalaCliOptionsForBspJson,
195202
createFolders = true
196203
)
204+
os.write.over(
205+
scalaCliBspLauncherOptsJsonDestination,
206+
scalaCliLaunchOptsForBspJson,
207+
createFolders = true
208+
)
197209
os.write.over(
198210
scalaCliBspInputsJsonDestination,
199211
scalaCliBspInputsJson,

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,9 @@ final case class SharedOptions(
407407
extraCompileOnlyJars = extraCompileOnlyClassPath,
408408
extraSourceJars = extraSourceJars.extractedClassPath ++ assumedSourceJars,
409409
extraRepositories =
410-
(dependencies.repository ++ ScalaCli.launcherPredefinedRepositories).map(_.trim).filter(
410+
(dependencies.repository ++ ScalaCli.launcherOptions.cliPredefinedRepository).map(
411+
_.trim
412+
).filter(
411413
_.nonEmpty
412414
),
413415
extraDependencies = ShadowingSeq.from(

modules/cli/src/main/scala/scala/cli/commands/version/Version.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ object Version extends ScalaCommand[VersionOptions] {
3434
else None
3535
}
3636
if options.cliVersion then println(Constants.version)
37-
else if options.scalaVersion then println(ScalaCli.getDefaultScalaVersion)
37+
else if options.scalaVersion then println(defaultScalaVersion)
3838
else {
3939
println(versionInfo)
4040
if !options.offline then
@@ -51,5 +51,5 @@ object Version extends ScalaCommand[VersionOptions] {
5151
val version = Constants.version
5252
val detailedVersionOpt = Constants.detailedVersion.filter(_ != version).fold("")(" (" + _ + ")")
5353
s"""$fullRunnerName version: $version$detailedVersionOpt
54-
|Scala version (default): ${ScalaCli.getDefaultScalaVersion}""".stripMargin
54+
|Scala version (default): $defaultScalaVersion""".stripMargin
5555
}

modules/cli/src/main/scala/scala/cli/launcher/LauncherOptions.scala

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package scala.cli.launcher
22

33
import caseapp.*
4+
import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec
5+
import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker
46

5-
import scala.cli.commands.shared.HelpGroup
7+
import scala.cli.commands.shared.{HelpGroup, SharedOptions}
68
import scala.cli.commands.{Constants, tags}
79

810
@HelpMessage("Run another Scala CLI version")
@@ -46,9 +48,18 @@ final case class LauncherOptions(
4648
progName: Option[String] = None,
4749
@Recurse
4850
powerOptions: PowerOptions = PowerOptions()
49-
)
51+
) {
52+
def toCliArgs: List[String] =
53+
cliVersion.toList.flatMap(v => List("--cli-version", v)) ++
54+
cliScalaVersion.toList.flatMap(v => List("--cli-scala-version", v)) ++
55+
cliUserScalaVersion.toList.flatMap(v => List("--cli-default-scala-version", v)) ++
56+
cliPredefinedRepository.flatMap(v => List("--cli-predefined-repository", v)) ++
57+
progName.toList.flatMap(v => List("--prog-name", v)) ++
58+
powerOptions.toCliArgs
59+
}
5060

5161
object LauncherOptions {
52-
implicit lazy val parser: Parser[LauncherOptions] = Parser.derive
53-
implicit lazy val help: Help[LauncherOptions] = Help.derive
62+
implicit lazy val parser: Parser[LauncherOptions] = Parser.derive
63+
implicit lazy val help: Help[LauncherOptions] = Help.derive
64+
implicit lazy val jsonCodec: JsonValueCodec[LauncherOptions] = JsonCodecMaker.make
5465
}

modules/cli/src/main/scala/scala/cli/launcher/PowerOptions.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ case class PowerOptions(
1717
@HelpMessage("Allows to use restricted & experimental features")
1818
@Tag(tags.must)
1919
power: Boolean = false
20-
)
20+
) {
21+
def toCliArgs: List[String] = if power then List("--power") else Nil
22+
}
2123

2224
object PowerOptions {
2325
implicit val parser: Parser[PowerOptions] = Parser.derive

0 commit comments

Comments
 (0)