Skip to content

Commit c21320e

Browse files
committed
Use InputsComposer in run command, add simple test
1 parent 70bb2dc commit c21320e

File tree

8 files changed

+164
-56
lines changed

8 files changed

+164
-56
lines changed

modules/build/src/main/scala/scala/build/BloopBuildClient.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ trait BloopBuildClient extends bsp4j.BuildClient {
1414

1515
object BloopBuildClient {
1616
def create(
17-
logger: Logger,
17+
projectNameOpt: Option[ProjectName],
18+
logger : Logger,
1819
keepDiagnostics: Boolean
1920
): BloopBuildClient =
2021
new ConsoleBloopBuildClient(
22+
projectNameOpt,
2123
logger,
2224
keepDiagnostics
2325
)

modules/build/src/main/scala/scala/build/Build.scala

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,9 @@ object Build {
229229
logger,
230230
options.suppressWarningOptions,
231231
options.internal.exclude
232-
)private def build(
232+
)
233+
234+
private def build(
233235
inputs: Module,
234236
crossSources: CrossSources,options: BuildOptions,
235237
logger: Logger,
@@ -273,10 +275,12 @@ object Build {
273275

274276
val baseOptions = overrideOptions.orElse(sharedOptions)
275277

276-
val inputs0 = updateInputs(
277-
inputs,
278-
overrideOptions.orElse(options) // update hash in inputs with options coming from the CLI or cross-building, not from the sources
279-
)
278+
val inputs0 = if (allInputs.mayAppendHash) {
279+
updateInputs(
280+
inputs,
281+
overrideOptions.orElse(options) // update hash in inputs with options coming from the CLI or cross-building, not from the sources
282+
)
283+
} else allInputs
280284

281285
val scopedSources = value(crossSources.scopedSources(baseOptions))
282286

@@ -561,9 +565,11 @@ object Build {
561565
crossBuilds: Boolean,
562566
buildTests: Boolean,
563567
partial: Option[Boolean],
564-
actionableDiagnostics: Option[Boolean]
568+
actionableDiagnostics: Option[Boolean],
569+
withProjectName: Boolean = false,
565570
)(using ScalaCliInvokeData): Either[BuildException, Builds] = either {
566571
val buildClient = BloopBuildClient.create(
572+
Option.when(withProjectName)(inputs.projectName),
567573
logger,
568574
keepDiagnostics = options.internal.keepDiagnostics
569575
)
@@ -646,6 +652,7 @@ object Build {
646652
)(action: Either[BuildException, Builds] => Unit)(using ScalaCliInvokeData): Watcher = {
647653

648654
val buildClient = BloopBuildClient.create(
655+
None,
649656
logger,
650657
keepDiagnostics = options.internal.keepDiagnostics
651658
)

modules/build/src/main/scala/scala/build/ConsoleBloopBuildClient.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import scala.collection.mutable
1616
import scala.jdk.CollectionConverters.*
1717

1818
class ConsoleBloopBuildClient(
19+
projectNameOpt: Option[ProjectName],
1920
logger: Logger,
2021
keepDiagnostics: Boolean = false,
2122
generatedSources: mutable.Map[ProjectName, Seq[GeneratedSource]] = mutable.Map()
@@ -27,7 +28,7 @@ class ConsoleBloopBuildClient(
2728
if (projectParams.isEmpty) ""
2829
else " (" + projectParams.mkString(", ") + ")"
2930

30-
private def projectName = "project" + projectNameSuffix
31+
private def projectDisplayName = s"${projectNameOpt.fold("project")(_.name)}$projectNameSuffix"
3132

3233
private var printedStart = false
3334

@@ -110,7 +111,7 @@ class ConsoleBloopBuildClient(
110111
for (msg <- Option(params.getMessage) if !msg.contains(" no-op compilation")) {
111112
printedStart = true
112113
val msg0 =
113-
if (params.getDataKind == "compile-task") s"Compiling $projectName"
114+
if (params.getDataKind == "compile-task") s"Compiling $projectDisplayName"
114115
else msg
115116
logger.message(gray + msg0 + reset)
116117
}
@@ -126,8 +127,8 @@ class ConsoleBloopBuildClient(
126127
val msg0 =
127128
if (params.getDataKind == "compile-report")
128129
params.getStatus match {
129-
case bsp4j.StatusCode.OK => s"Compiled $projectName"
130-
case bsp4j.StatusCode.ERROR => s"Error compiling $projectName"
130+
case bsp4j.StatusCode.OK => s"Compiled $projectDisplayName"
131+
case bsp4j.StatusCode.ERROR => s"Error compiling $projectDisplayName"
131132
case bsp4j.StatusCode.CANCELLED => s"Compilation cancelled$projectNameSuffix"
132133
}
133134
else msg

modules/build/src/main/scala/scala/build/input/compose/Inputs.scala

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,21 @@ sealed trait Inputs {
1717
/** Order in which to build all modules */
1818
def modulesBuildOrder: Seq[Module]
1919

20-
/** Order in which to build the target module with its dependencies, e.g. to execute a command on
20+
/** Order in which to build the dependencies of the target module, e.g. to execute a command on
2121
* [[targetModule]]
2222
*/
23-
def targetBuildOrder: Seq[Module]
23+
def targetDependenciesBuildOrder: Seq[Module]
2424
def workspaceOrigin: Option[WorkspaceOrigin]
2525
def workspace: os.Path
2626

27-
def preprocessInputs(preprocess: Module => (Module, BuildOptions))
28-
: (Inputs, Seq[BuildOptions])
27+
def preprocessInputs(preprocess: Module => (Module, BuildOptions)): (Inputs, Seq[BuildOptions])
2928
}
3029

3130
/** Result of using [[InputsComposer]] with module config file present */
3231
case class ComposedInputs(
33-
modules: Seq[Module],
34-
targetModule: Module,
35-
workspace: os.Path
32+
modules: Seq[Module],
33+
targetModule: Module,
34+
workspace: os.Path
3635
) extends Inputs {
3736

3837
// Forced to be the directory where module config file (modules.yaml) resides
@@ -42,8 +41,8 @@ case class ComposedInputs(
4241
private val dependencyGraph = modules.map(m => m.projectName -> m.moduleDependencies).toMap
4342

4443
private def buildOrderForModule(
45-
root: Module,
46-
visitedPreviously: Set[ProjectName]
44+
root: Module,
45+
visitedPreviously: Set[ProjectName]
4746
): Seq[ProjectName] = {
4847
val visited = mutable.Set.from(visitedPreviously) // Track visited nodes
4948
val result =
@@ -67,8 +66,10 @@ case class ComposedInputs(
6766
acc.appendedAll(buildOrder)
6867
}.map(nameMap)
6968

70-
override lazy val targetBuildOrder: Seq[Module] =
71-
buildOrderForModule(targetModule, Set.empty).map(nameMap)
69+
override lazy val targetDependenciesBuildOrder: Seq[Module] = {
70+
val buildOrderWithTarget = buildOrderForModule(targetModule, Set.empty).map(nameMap)
71+
buildOrderWithTarget.dropRight(1)
72+
}
7273

7374
def preprocessInputs(preprocess: Module => (Module, BuildOptions))
7475
: (ComposedInputs, Seq[BuildOptions]) = {
@@ -96,7 +97,7 @@ case class SimpleInputs(
9697

9798
override val modulesBuildOrder: Seq[Module] = modules
9899

99-
override val targetBuildOrder: Seq[Module] = modules
100+
override val targetDependenciesBuildOrder: Seq[Module] = Nil
100101

101102
override val workspace: os.Path = singleModule.workspace
102103

modules/cli/src/main/scala/scala/cli/commands/run/Run.scala

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import java.io.File
88
import java.util.Locale
99
import java.util.concurrent.CompletableFuture
1010
import java.util.concurrent.atomic.AtomicReference
11-
1211
import scala.build.EitherCps.{either, value}
1312
import scala.build.*
14-
import scala.build.errors.BuildException
13+
import scala.build.errors.{BuildException, InputsException}
14+
import scala.build.input.compose.{ComposedInputs, SimpleInputs}
1515
import scala.build.input.{Module, ScalaCliInvokeData, SubCommand}
1616
import scala.build.internal.util.ConsoleUtils.ScalaCliConsole
1717
import scala.build.internal.{Constants, Runner, ScalaJsLinkerConfig}
@@ -112,12 +112,12 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
112112
}
113113

114114
def runCommand(
115-
options: RunOptions,
116-
inputArgs: Seq[String],
117-
programArgs: Seq[String],
118-
defaultInputs: () => Option[Module],
119-
logger: Logger,
120-
invokeData: ScalaCliInvokeData
115+
options: RunOptions,
116+
inputArgs: Seq[String],
117+
programArgs: Seq[String],
118+
defaultInputs: () => Option[Module],
119+
logger: Logger,
120+
invokeData: ScalaCliInvokeData
121121
): Unit = {
122122
val initialBuildOptions = {
123123
val buildOptions = buildOptionsOrExit(options)
@@ -133,7 +133,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
133133
else buildOptions
134134
}
135135

136-
val inputs = options.shared.inputs(
136+
val inputs = options.shared.composeInputs(
137137
inputArgs,
138138
defaultInputs
139139
)(
@@ -149,7 +149,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
149149
allowTerminate: Boolean,
150150
runMode: RunMode,
151151
showCommand: Boolean,
152-
scratchDirOpt: Option[os.Path]
152+
scratchDirOpt: Option[os.Path],
153+
classpathFromModuleDeps: Seq[os.Path] = Nil
153154
): Either[BuildException, Option[(Process, CompletableFuture[_])]] = either {
154155
val potentialMainClasses = build.foundMainClasses()
155156
if (options.sharedRun.mainClass.mainClassLs.contains(true))
@@ -170,7 +171,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
170171
runMode,
171172
showCommand,
172173
scratchDirOpt,
173-
asJar = options.shared.asJar
174+
asJar = options.shared.asJar,
175+
classpathFromModuleDeps
174176
)
175177
}
176178

@@ -243,8 +245,13 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
243245
*/
244246
val mainThreadOpt = AtomicReference(Option.empty[Thread])
245247

248+
val moduleToWatch = inputs match
249+
case ComposedInputs(modules, targetModule, workspace) =>
250+
logger.exit(InputsException("Watch mode is not available in compose mode"))
251+
case SimpleInputs(singleModule) => singleModule
252+
246253
val watcher = Build.watch(
247-
inputs,
254+
moduleToWatch,
248255
initialBuildOptions,
249256
compilerMaker,
250257
None,
@@ -317,28 +324,56 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
317324
}
318325
}
319326
else {
320-
val builds =
327+
val moduleDependencies: Seq[Build.Successful] =
328+
for (module <- inputs.targetDependenciesBuildOrder) yield {
329+
val builds =
330+
Build.build(
331+
module,
332+
initialBuildOptions,
333+
compilerMaker,
334+
None,
335+
logger,
336+
crossBuilds = cross,
337+
buildTests = false,
338+
partial = None,
339+
actionableDiagnostics = actionableDiagnostics,
340+
withProjectName = true
341+
)
342+
.orExit(logger)
343+
344+
builds.main match {
345+
case s: Build.Successful => s
346+
case _: Build.Failed =>
347+
System.err.println(s"Compilation of module ${module.projectName} failed")
348+
sys.exit(1)
349+
}
350+
}
351+
352+
val targetBuilds =
321353
Build.build(
322-
inputs,
354+
inputs.targetModule,
323355
initialBuildOptions,
324356
compilerMaker,
325357
None,
326358
logger,
327359
crossBuilds = cross,
328360
buildTests = false,
329361
partial = None,
330-
actionableDiagnostics = actionableDiagnostics
362+
actionableDiagnostics = actionableDiagnostics,
363+
withProjectName = inputs.isInstanceOf[ComposedInputs]
331364
)
332365
.orExit(logger)
333-
builds.main match {
366+
targetBuilds.main match {
334367
case s: Build.Successful =>
335368
s.copyOutput(options.shared)
369+
val mainWithModuleDeps = s.copy()
336370
val res = maybeRun(
337371
s,
338372
allowTerminate = true,
339373
runMode = runMode(options),
340374
showCommand = options.sharedRun.command,
341-
scratchDirOpt = scratchDirOpt(options)
375+
scratchDirOpt = scratchDirOpt(options),
376+
moduleDependencies.flatMap(_.fullClassPath).distinct
342377
)
343378
.orExit(logger)
344379
for ((process, onExit) <- res)
@@ -360,7 +395,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
360395
runMode: RunMode,
361396
showCommand: Boolean,
362397
scratchDirOpt: Option[os.Path],
363-
asJar: Boolean
398+
asJar: Boolean,
399+
classpathFromModuleDeps: Seq[os.Path]
364400
): Either[BuildException, Either[Seq[String], (Process, Option[() => Unit])]] = either {
365401

366402
val mainClassOpt = build.options.mainClass.filter(_.nonEmpty) // trim it too?
@@ -386,7 +422,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
386422
runMode,
387423
showCommand,
388424
scratchDirOpt,
389-
asJar
425+
asJar,
426+
classpathFromModuleDeps
390427
)
391428
value(res)
392429
}
@@ -421,7 +458,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
421458
runMode: RunMode,
422459
showCommand: Boolean,
423460
scratchDirOpt: Option[os.Path],
424-
asJar: Boolean
461+
asJar: Boolean,
462+
classpathFromModuleDeps: Seq[os.Path]
425463
): Either[BuildException, Either[Seq[String], (Process, Option[() => Unit])]] = either {
426464

427465
build.options.platform.value match {
@@ -568,7 +606,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
568606
val command = Runner.jvmCommand(
569607
build.options.javaHome().value.javaCommand,
570608
allJavaOpts,
571-
build.fullClassPathMaybeAsJar(asJar),
609+
build.fullClassPathMaybeAsJar(asJar) ++ classpathFromModuleDeps,
572610
mainClass,
573611
args,
574612
extraEnv = pythonExtraEnv,
@@ -581,7 +619,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
581619
val proc = Runner.runJvm(
582620
build.options.javaHome().value.javaCommand,
583621
allJavaOpts,
584-
build.fullClassPathMaybeAsJar(asJar),
622+
build.fullClassPathMaybeAsJar(asJar) ++ classpathFromModuleDeps,
585623
mainClass,
586624
args,
587625
logger,

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

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ import scala.jdk.CollectionConverters.*
2626
object SetupIde extends ScalaCommand[SetupIdeOptions] {
2727

2828
def downloadDeps(
29-
inputs: Module,
30-
options: BuildOptions,
31-
logger: Logger
29+
inputs: Module,
30+
options: BuildOptions,
31+
logger: Logger
3232
): Either[BuildException, Artifacts] = {
3333

3434
// ignoring errors related to sources themselves
@@ -83,12 +83,32 @@ object SetupIde extends ScalaCommand[SetupIdeOptions] {
8383
}
8484

8585
def runSafe(
86-
options: SharedOptions,
87-
inputs: Module,
88-
logger: Logger,
89-
buildOptions: BuildOptions,
90-
previousCommandName: Option[String],
91-
args: Seq[String]
86+
options: SharedOptions,
87+
inputs: compose.Inputs,
88+
logger: Logger,
89+
buildOptions: BuildOptions,
90+
previousCommandName: Option[String],
91+
args: Seq[String]
92+
): Unit =
93+
writeBspConfiguration(
94+
SetupIdeOptions(shared = options),
95+
inputs,
96+
buildOptions,
97+
previousCommandName,
98+
args
99+
) match {
100+
case Left(ex) =>
101+
logger.debug(s"Ignoring error during setup-ide: ${ex.message}")
102+
case Right(_) =>
103+
}
104+
105+
def runSafe(
106+
options: SharedOptions,
107+
inputs: Module,
108+
logger: Logger,
109+
buildOptions: BuildOptions,
110+
previousCommandName: Option[String],
111+
args: Seq[String]
92112
): Unit =
93113
writeBspConfiguration(
94114
SetupIdeOptions(shared = options),

0 commit comments

Comments
 (0)