Skip to content

Commit 2246a45

Browse files
committed
Use InputsComposer in run command, add simple test
1 parent 0891684 commit 2246a45

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
@@ -234,7 +234,9 @@ object Build {
234234
logger,
235235
options.suppressWarningOptions,
236236
options.internal.exclude
237-
)private def build(
237+
)
238+
239+
private def build(
238240
inputs: Module,
239241
crossSources: CrossSources,options: BuildOptions,
240242
logger: Logger,
@@ -278,10 +280,12 @@ object Build {
278280

279281
val baseOptions = overrideOptions.orElse(sharedOptions)
280282

281-
val inputs0 = updateInputs(
282-
inputs,
283-
overrideOptions.orElse(options) // update hash in inputs with options coming from the CLI or cross-building, not from the sources
284-
)
283+
val inputs0 = if (allInputs.mayAppendHash) {
284+
updateInputs(
285+
inputs,
286+
overrideOptions.orElse(options) // update hash in inputs with options coming from the CLI or cross-building, not from the sources
287+
)
288+
} else allInputs
285289

286290
val scopedSources = value(crossSources.scopedSources(baseOptions))
287291

@@ -566,9 +570,11 @@ object Build {
566570
crossBuilds: Boolean,
567571
buildTests: Boolean,
568572
partial: Option[Boolean],
569-
actionableDiagnostics: Option[Boolean]
573+
actionableDiagnostics: Option[Boolean],
574+
withProjectName: Boolean = false,
570575
)(using ScalaCliInvokeData): Either[BuildException, Builds] = either {
571576
val buildClient = BloopBuildClient.create(
577+
Option.when(withProjectName)(inputs.projectName),
572578
logger,
573579
keepDiagnostics = options.internal.keepDiagnostics
574580
)
@@ -651,6 +657,7 @@ object Build {
651657
)(action: Either[BuildException, Builds] => Unit)(using ScalaCliInvokeData): Watcher = {
652658

653659
val buildClient = BloopBuildClient.create(
660+
None,
654661
logger,
655662
keepDiagnostics = options.internal.keepDiagnostics
656663
)

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.{Constants, Runner, ScalaJsLinkerConfig}
1717
import scala.build.internals.ConsoleUtils.ScalaCliConsole
@@ -113,12 +113,12 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
113113
}
114114

115115
def runCommand(
116-
options0: RunOptions,
117-
inputArgs: Seq[String],
118-
programArgs: Seq[String],
119-
defaultInputs: () => Option[Module],
120-
logger: Logger,
121-
invokeData: ScalaCliInvokeData
116+
options0: RunOptions,
117+
inputArgs: Seq[String],
118+
programArgs: Seq[String],
119+
defaultInputs: () => Option[Module],
120+
logger: Logger,
121+
invokeData: ScalaCliInvokeData
122122
): Unit = {
123123
val shouldDefaultServerFalse =
124124
inputArgs.isEmpty && options0.shared.compilationServer.server.isEmpty &&
@@ -143,7 +143,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
143143
else buildOptions
144144
}
145145

146-
val inputs = options.shared.inputs(
146+
val inputs = options.shared.composeInputs(
147147
inputArgs,
148148
defaultInputs
149149
)(
@@ -159,7 +159,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
159159
allowTerminate: Boolean,
160160
runMode: RunMode,
161161
showCommand: Boolean,
162-
scratchDirOpt: Option[os.Path]
162+
scratchDirOpt: Option[os.Path],
163+
classpathFromModuleDeps: Seq[os.Path] = Nil
163164
): Either[BuildException, Option[(Process, CompletableFuture[_])]] = either {
164165
val potentialMainClasses = build.foundMainClasses()
165166
if (options.sharedRun.mainClass.mainClassLs.contains(true))
@@ -180,7 +181,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
180181
runMode,
181182
showCommand,
182183
scratchDirOpt,
183-
asJar = options.shared.asJar
184+
asJar = options.shared.asJar,
185+
classpathFromModuleDeps
184186
)
185187
}
186188

@@ -253,8 +255,13 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
253255
*/
254256
val mainThreadOpt = AtomicReference(Option.empty[Thread])
255257

258+
val moduleToWatch = inputs match
259+
case ComposedInputs(modules, targetModule, workspace) =>
260+
logger.exit(InputsException("Watch mode is not available in compose mode"))
261+
case SimpleInputs(singleModule) => singleModule
262+
256263
val watcher = Build.watch(
257-
inputs,
264+
moduleToWatch,
258265
initialBuildOptions,
259266
compilerMaker,
260267
None,
@@ -327,28 +334,56 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
327334
}
328335
}
329336
else {
330-
val builds =
337+
val moduleDependencies: Seq[Build.Successful] =
338+
for (module <- inputs.targetDependenciesBuildOrder) yield {
339+
val builds =
340+
Build.build(
341+
module,
342+
initialBuildOptions,
343+
compilerMaker,
344+
None,
345+
logger,
346+
crossBuilds = cross,
347+
buildTests = false,
348+
partial = None,
349+
actionableDiagnostics = actionableDiagnostics,
350+
withProjectName = true
351+
)
352+
.orExit(logger)
353+
354+
builds.main match {
355+
case s: Build.Successful => s
356+
case _: Build.Failed =>
357+
System.err.println(s"Compilation of module ${module.projectName} failed")
358+
sys.exit(1)
359+
}
360+
}
361+
362+
val targetBuilds =
331363
Build.build(
332-
inputs,
364+
inputs.targetModule,
333365
initialBuildOptions,
334366
compilerMaker,
335367
None,
336368
logger,
337369
crossBuilds = cross,
338370
buildTests = false,
339371
partial = None,
340-
actionableDiagnostics = actionableDiagnostics
372+
actionableDiagnostics = actionableDiagnostics,
373+
withProjectName = inputs.isInstanceOf[ComposedInputs]
341374
)
342375
.orExit(logger)
343-
builds.main match {
376+
targetBuilds.main match {
344377
case s: Build.Successful =>
345378
s.copyOutput(options.shared)
379+
val mainWithModuleDeps = s.copy()
346380
val res = maybeRun(
347381
s,
348382
allowTerminate = true,
349383
runMode = runMode(options),
350384
showCommand = options.sharedRun.command,
351-
scratchDirOpt = scratchDirOpt(options)
385+
scratchDirOpt = scratchDirOpt(options),
386+
moduleDependencies.flatMap(_.fullClassPath).distinct
352387
)
353388
.orExit(logger)
354389
for ((process, onExit) <- res)
@@ -370,7 +405,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
370405
runMode: RunMode,
371406
showCommand: Boolean,
372407
scratchDirOpt: Option[os.Path],
373-
asJar: Boolean
408+
asJar: Boolean,
409+
classpathFromModuleDeps: Seq[os.Path]
374410
): Either[BuildException, Either[Seq[String], (Process, Option[() => Unit])]] = either {
375411

376412
val mainClassOpt = build.options.mainClass.filter(_.nonEmpty) // trim it too?
@@ -396,7 +432,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
396432
runMode,
397433
showCommand,
398434
scratchDirOpt,
399-
asJar
435+
asJar,
436+
classpathFromModuleDeps
400437
)
401438
value(res)
402439
}
@@ -431,7 +468,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
431468
runMode: RunMode,
432469
showCommand: Boolean,
433470
scratchDirOpt: Option[os.Path],
434-
asJar: Boolean
471+
asJar: Boolean,
472+
classpathFromModuleDeps: Seq[os.Path]
435473
): Either[BuildException, Either[Seq[String], (Process, Option[() => Unit])]] = either {
436474

437475
build.options.platform.value match {
@@ -578,7 +616,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
578616
val command = Runner.jvmCommand(
579617
build.options.javaHome().value.javaCommand,
580618
allJavaOpts,
581-
build.fullClassPathMaybeAsJar(asJar),
619+
build.fullClassPathMaybeAsJar(asJar) ++ classpathFromModuleDeps,
582620
mainClass,
583621
args,
584622
extraEnv = pythonExtraEnv,
@@ -591,7 +629,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
591629
val proc = Runner.runJvm(
592630
build.options.javaHome().value.javaCommand,
593631
allJavaOpts,
594-
build.fullClassPathMaybeAsJar(asJar),
632+
build.fullClassPathMaybeAsJar(asJar) ++ classpathFromModuleDeps,
595633
mainClass,
596634
args,
597635
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
@@ -27,9 +27,9 @@ import scala.jdk.CollectionConverters.*
2727
object SetupIde extends ScalaCommand[SetupIdeOptions] {
2828

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

3535
// ignoring errors related to sources themselves
@@ -84,12 +84,32 @@ object SetupIde extends ScalaCommand[SetupIdeOptions] {
8484
}
8585

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

0 commit comments

Comments
 (0)