Skip to content

Commit f754c15

Browse files
committed
WIP
1 parent 0d8d01f commit f754c15

File tree

7 files changed

+265
-59
lines changed

7 files changed

+265
-59
lines changed

modules/build/src/main/scala/scala/build/bsp/BloopSession.scala

+11-11
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ package scala.build.bsp
33
import com.swoval.files.PathWatchers
44

55
import java.util.concurrent.atomic.AtomicReference
6-
76
import scala.build.Build
87
import scala.build.compiler.BloopCompiler
9-
import scala.build.input.{Module, OnDisk, SingleFile, Virtual, compose}
8+
import scala.build.compose.{Inputs, input as compose}
9+
import scala.build.input.{Module, OnDisk, SingleFile, Virtual}
1010

1111
final class BloopSession(
12-
val inputs: compose.Inputs,
13-
// val inputsHash: String, TODO Fix inputs hash comparing
14-
val remoteServer: BloopCompiler,
15-
val bspServer: BspServer,
16-
val watcher: Build.Watcher
12+
val inputs: Inputs,
13+
// val inputsHash: String, TODO Fix inputs hash comparing
14+
val remoteServer: BloopCompiler,
15+
val bspServer: BspServer,
16+
val watcher: Build.Watcher
1717
) {
1818
def resetDiagnostics(localClient: BspClient): Unit = for {
1919
module <- inputs.modules
@@ -65,10 +65,10 @@ final class BloopSession(
6565
object BloopSession {
6666

6767
def apply(
68-
inputs: compose.Inputs,
69-
remoteServer: BloopCompiler,
70-
bspServer: BspServer,
71-
watcher: Build.Watcher
68+
inputs: Inputs,
69+
remoteServer: BloopCompiler,
70+
bspServer: BspServer,
71+
watcher: Build.Watcher
7272
): BloopSession = new BloopSession(inputs, remoteServer, bspServer, watcher)
7373

7474
final class Reference {

modules/build/src/main/scala/scala/build/bsp/BspImpl.scala

+17-23
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,12 @@ import org.eclipse.lsp4j.jsonrpc.messages.ResponseError
1010
import java.io.{InputStream, OutputStream}
1111
import java.util.UUID
1212
import java.util.concurrent.{CompletableFuture, Executor}
13-
1413
import scala.build.EitherCps.{either, value}
1514
import scala.build.*
1615
import scala.build.bsp.buildtargets.{ManagesBuildTargets, ProjectName}
1716
import scala.build.compiler.BloopCompiler
18-
import scala.build.errors.{
19-
BuildException,
20-
CompositeBuildException,
21-
Diagnostic,
22-
ParsingInputsException
23-
}
24-
import scala.build.input.{Module, ScalaCliInvokeData, compose}
17+
import scala.build.compose.Inputs
18+
import scala.build.errors.{BuildException, CompositeBuildException, Diagnostic, ParsingInputsException}
2519
import scala.build.internal.Constants
2620
import scala.build.options.{BuildOptions, Scope}
2721
import scala.collection.mutable.ListBuffer
@@ -44,12 +38,12 @@ import scala.util.{Failure, Success}
4438
* the output stream of bytes
4539
*/
4640
final class BspImpl(
47-
argsToInputs: Seq[String] => Either[BuildException, compose.Inputs],
48-
bspReloadableOptionsReference: BspReloadableOptions.Reference,
49-
threads: BspThreads,
50-
in: InputStream,
51-
out: OutputStream,
52-
actionableDiagnostics: Option[Boolean]
41+
argsToInputs: Seq[String] => Either[BuildException, Inputs],
42+
bspReloadableOptionsReference: BspReloadableOptions.Reference,
43+
threads: BspThreads,
44+
in: InputStream,
45+
out: OutputStream,
46+
actionableDiagnostics: Option[Boolean]
5347
)(using ScalaCliInvokeData) extends Bsp {
5448

5549
import BspImpl.{
@@ -457,9 +451,9 @@ final class BspImpl(
457451
* a new [[BloopSession]]
458452
*/
459453
private def newBloopSession(
460-
inputs: compose.Inputs,
461-
reloadableOptions: BspReloadableOptions,
462-
presetIntelliJ: Boolean = false
454+
inputs: Inputs,
455+
reloadableOptions: BspReloadableOptions,
456+
presetIntelliJ: Boolean = false
463457
): BloopSession = {
464458
val logger = reloadableOptions.logger
465459
val buildOptions = reloadableOptions.buildOptions
@@ -510,8 +504,8 @@ final class BspImpl(
510504
* change on subsequent workspace/reload requests)
511505
*/
512506
override def run(
513-
initialInputs: compose.Inputs,
514-
initialBspOptions: BspReloadableOptions
507+
initialInputs: Inputs,
508+
initialBspOptions: BspReloadableOptions
515509
): Future[Unit] = {
516510
val logger = initialBspOptions.logger
517511
val verbosity = initialBspOptions.verbosity
@@ -613,10 +607,10 @@ final class BspImpl(
613607
* a future containing a valid workspace/reload response
614608
*/
615609
private def reloadBsp(
616-
currentBloopSession: BloopSession,
617-
previousInputs: compose.Inputs,
618-
newInputs: compose.Inputs,
619-
reloadableOptions: BspReloadableOptions
610+
currentBloopSession: BloopSession,
611+
previousInputs: Inputs,
612+
newInputs: Inputs,
613+
reloadableOptions: BspReloadableOptions
620614
): CompletableFuture[AnyRef] = {
621615
val previousTargetIds = currentBloopSession.bspServer.targetIds
622616
val wasIntelliJ = currentBloopSession.bspServer.isIntelliJ
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
package scala.build.compose
2+
3+
import scala.build.{BloopBuildClient, CrossSources, Logger}
4+
import scala.build.bsp.BspServer
5+
import scala.build.bsp.buildtargets.ProjectName
6+
import scala.build.compose.input as compose
7+
import scala.build.errors.BuildException
8+
import scala.build.options.BuildOptions
9+
import scala.build.compiler.ScalaCompiler
10+
import scala.build.{Artifacts, Project}
11+
import scala.build.input.Module
12+
13+
object ComposeBuild {
14+
15+
private final case class PreBuildData(
16+
sources: Sources,
17+
buildOptions: BuildOptions,
18+
classesDir: os.Path,
19+
scalaParams: Option[ScalaParameters],
20+
artifacts: Artifacts,
21+
project: Project,
22+
generatedSources: Seq[GeneratedSource],
23+
buildChanged: Boolean
24+
)
25+
26+
private final case class PreBuildProject(prebuildModules: Seq[PreBuildModule])
27+
28+
private final case class PreBuildModule(
29+
module: Module,
30+
mainScope: PreBuildData,
31+
testScope: PreBuildData,
32+
diagnostics: Seq[Diagnostic]
33+
)
34+
}
35+
36+
case class ComposeBuild(
37+
buildOptions: BuildOptions,
38+
inputs: Inputs,
39+
logger: Logger,
40+
compiler: ScalaCompiler,
41+
buildClient: BloopBuildClient,
42+
bspServer: Option[BspServer],
43+
verbosity: Int = 0,
44+
maybeRecoverOnError: ProjectName => BuildException => Option[BuildException] = _ => e => Some(e)
45+
) {
46+
47+
import ComposeBuild.*
48+
49+
/** Prepares the build for all modules in the inputs, */
50+
def prepareBuild(): Either[(BuildException, ProjectName), PreBuildProject] = either {
51+
logger.log("Preparing composed build")
52+
53+
val prebuildModules: Seq[PreBuildModule] = for (module <- inputs.modulesBuildOrder) yield {
54+
value(prepareModule(module))
55+
}
56+
57+
val prebuildModulesWithLinkedDeps = {
58+
val preBuildDataMap: Map[ProjectName, PreBuildModule] = prebuildModules.map(m => m.module.projectName -> m)
59+
60+
prebuildModules.map { prebuildModule =>
61+
val additionalMainClassPath = prebuildModule.module.moduleDependencies
62+
.map(preBuildDataMap)
63+
.flatMap(_.mainScope.project.classPath)
64+
val oldMainProject = prebuildModule.mainScope.project
65+
val newMainProject = oldMainProject.copy(
66+
classPath = (oldMainProject.classPath.toSet ++ additionalMainClassPath).toSeq
67+
)
68+
69+
val additionalTestClassPath = prebuildModule.module.moduleDependencies
70+
.map(preBuildDataMap)
71+
.flatMap(_.testScope.project.classPath)
72+
val oldTestProject = prebuildModule.testScope.project
73+
val newTestProject = oldTestProject.copy(
74+
classPath = (oldTestProject.classPath.toSet ++ additionalMainClassPath ++ additionalTestClassPath).toSeq
75+
)
76+
77+
prebuildModule.copy(
78+
mainScope = prebuildModule.mainScope.copy(project = newMainProject),
79+
testScope = prebuildModule.mainScope.copy(project = newTestProject)
80+
)
81+
}
82+
}
83+
84+
PreBuildProject(prebuildModulesWithLinkedDeps)
85+
}
86+
87+
def prepareModule(module: Module): Either[(BuildException, ProjectName), PreBuildModule] = either {
88+
val mainProjectName = module.projectName
89+
val testProjectName = module.scopeProjectName(Scope.Test)
90+
91+
// allInputs contains elements from using directives
92+
val (crossSources, allInputs) = value {
93+
CrossSources.forModuleInputs(
94+
inputs = module,
95+
preprocessors = Sources.defaultPreprocessors(
96+
buildOptions.archiveCache,
97+
buildOptions.internal.javaClassNameVersionOpt,
98+
() => buildOptions.javaHome().value.javaCommand
99+
),
100+
logger = logger,
101+
suppressWarningOptions = buildOptions.suppressWarningOptions,
102+
exclude = buildOptions.internal.exclude,
103+
maybeRecoverOnError = maybeRecoverOnError(mainProjectName)
104+
).left.map(_ -> mainProjectName)
105+
}
106+
107+
val sharedOptions = crossSources.sharedOptions(buildOptions)
108+
109+
if (verbosity >= 4)
110+
pprint.err.log(crossSources)
111+
112+
val scopedSources =
113+
value(crossSources.scopedSources(buildOptions).left.map(_ -> mainProjectName))
114+
115+
if (verbosity >= 4)
116+
pprint.err.log(scopedSources)
117+
118+
val sourcesMain = value {
119+
scopedSources.sources(Scope.Main, sharedOptions, allInputs.workspace, persistentLogger)
120+
.left.map(_ -> mainProjectName)
121+
}
122+
123+
val sourcesTest = value {
124+
scopedSources.sources(Scope.Test, sharedOptions, allInputs.workspace, persistentLogger)
125+
.left.map(_ -> testProjectName)
126+
}
127+
128+
if (verbosity >= 4)
129+
pprint.err.log(sourcesMain)
130+
131+
val options0Main = sourcesMain.buildOptions
132+
val options0Test = sourcesTest.buildOptions.orElse(options0Main)
133+
134+
val generatedSourcesMain =
135+
sourcesMain.generateSources(allInputs.generatedSrcRoot(Scope.Main))
136+
val generatedSourcesTest =
137+
sourcesTest.generateSources(allInputs.generatedSrcRoot(Scope.Test))
138+
139+
// Notify the Bsp server (if there is any) about changes to the project params
140+
bspServer.foreach(_.setExtraDependencySources(options0Main.classPathOptions.extraSourceJars))
141+
bspServer.foreach(_.setExtraTestDependencySources(options0Test.classPathOptions.extraSourceJars))
142+
bspServer.foreach(_.setGeneratedSources(mainProjectName, generatedSourcesMain))
143+
bspServer.foreach(_.setGeneratedSources(testProjectName, generatedSourcesTest))
144+
145+
// Notify the build client about generated sources so that it can modify diagnostics coming to the remote client e.g. IDE or console (not really a client, but you get it)
146+
buildClient.setGeneratedSources(mainProjectName, generatedSourcesMain)
147+
buildClient.setGeneratedSources(testProjectName, generatedSourcesTest)
148+
149+
val (classesDir0Main, scalaParamsMain, artifactsMain, projectMain, buildChangedMain) =
150+
value {
151+
val res = Build.prepareBuild(
152+
allInputs,
153+
sourcesMain,
154+
generatedSourcesMain,
155+
options0Main,
156+
None,
157+
Scope.Main,
158+
currentBloopSession.remoteServer,
159+
persistentLogger,
160+
buildClient,
161+
maybeRecoverOnError(mainProjectName)
162+
)
163+
res.left.map(_ -> mainProjectName)
164+
}
165+
166+
val (classesDir0Test, scalaParamsTest, artifactsTest, projectTest, buildChangedTest) =
167+
value {
168+
val res = Build.prepareBuild(
169+
allInputs,
170+
sourcesTest,
171+
generatedSourcesTest,
172+
options0Test,
173+
None,
174+
Scope.Test,
175+
currentBloopSession.remoteServer,
176+
persistentLogger,
177+
buildClient,
178+
maybeRecoverOnError(testProjectName)
179+
)
180+
res.left.map(_ -> testProjectName)
181+
}
182+
183+
val mainScope = PreBuildData(
184+
sourcesMain,
185+
options0Main,
186+
classesDir0Main,
187+
scalaParamsMain,
188+
artifactsMain,
189+
projectMain,
190+
generatedSourcesMain,
191+
buildChangedMain
192+
)
193+
194+
val testScope = PreBuildData(
195+
sourcesTest,
196+
options0Test,
197+
classesDir0Test,
198+
scalaParamsTest,
199+
artifactsTest,
200+
projectTest,
201+
generatedSourcesTest,
202+
buildChangedTest
203+
)
204+
205+
if (actionableDiagnostics.getOrElse(true)) {
206+
val projectOptions = options0Test.orElse(options0Main)
207+
projectOptions.logActionableDiagnostics(persistentLogger)
208+
}
209+
210+
PreBuildModule(module, mainScope, testScope, persistentLogger.diagnostics)
211+
}
212+
213+
}

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
package scala.build.input.compose
1+
package scala.build.compose
22

33
import scala.build.bsp.buildtargets.ProjectName
4+
import scala.build.compose.{Inputs, InputsComposer}
45
import scala.build.input.{Module, WorkspaceOrigin}
56
import scala.build.options.BuildOptions
67
import scala.collection.mutable
@@ -71,6 +72,11 @@ case class ComposedInputs(
7172
buildOrderWithTarget.dropRight(1)
7273
}
7374

75+
def buildOrderForModule(module: Module): Seq[Module] = {
76+
buildOrderForModule(module, Set.empty).map(nameMap)
77+
buildOrderWithTarget.dropRight(1)
78+
}
79+
7480
def preprocessInputs(preprocess: Module => (Module, BuildOptions))
7581
: (ComposedInputs, Seq[BuildOptions]) = {
7682
val (preprocessedModules, buildOptions) =

modules/build/src/main/scala/scala/build/input/compose/InputsComposer.scala renamed to modules/build/src/main/scala/scala/build/compose/InputsComposer.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
package scala.build.input.compose
1+
package scala.build.compose
22

33
import toml.Value
44
import toml.Value.*
55

66
import scala.build.EitherCps.*
77
import scala.build.EitherSequence
88
import scala.build.bsp.buildtargets.ProjectName
9+
import scala.build.compose.{ComposedInputs, Inputs, InputsComposer, SimpleInputs}
910
import scala.build.errors.{BuildException, CompositeBuildException, ModuleConfigurationError}
1011
import scala.build.input.Module
11-
import scala.build.input.compose.InputsComposer
1212
import scala.build.internal.Constants
1313
import scala.build.options.BuildOptions
1414
import scala.collection.mutable

0 commit comments

Comments
 (0)