@@ -9,10 +9,10 @@ import mill.define
9
9
import mill .define .WorkspaceRoot
10
10
import mill .util .BuildInfo
11
11
import mill .runner .meta .ScalaCompilerWorker
12
- import mill .internal .{Colors , PromptLogger }
12
+ import mill .internal .{Colors , MultiStream , PromptLogger }
13
13
import mill .server .Server
14
14
15
- import java .io .{PipedInputStream , PrintStream }
15
+ import java .io .{InputStream , PipedInputStream , PrintStream }
16
16
import java .lang .reflect .InvocationTargetException
17
17
import java .util .Locale
18
18
import scala .collection .immutable
@@ -79,6 +79,40 @@ object MillMain {
79
79
80
80
lazy val maybeScalaCompilerWorker = ScalaCompilerWorker .bootstrapWorker()
81
81
82
+ private def withStreams [T ](
83
+ bspMode : Boolean ,
84
+ streams : SystemStreams
85
+ )(thunk : SystemStreams => T ): T =
86
+ if (bspMode) {
87
+ // In BSP mode, don't let anything other than the BSP server write to stdout and read from stdin
88
+
89
+ val outFileStream = os.write.outputStream(
90
+ WorkspaceRoot .workspaceRoot / OutFiles .out / " mill-bsp/out.log" ,
91
+ createFolders = true
92
+ )
93
+ val errFileStream = os.write.outputStream(
94
+ WorkspaceRoot .workspaceRoot / OutFiles .out / " mill-bsp/err.log" ,
95
+ createFolders = true
96
+ )
97
+
98
+ try {
99
+ val streams0 = new SystemStreams (
100
+ out = new MultiStream (streams.err, outFileStream),
101
+ err = new MultiStream (streams.err, errFileStream),
102
+ in = InputStream .nullInputStream()
103
+ )
104
+ mill.define.SystemStreams .withStreams(streams0) {
105
+ thunk(streams0)
106
+ }
107
+ } finally {
108
+ errFileStream.close()
109
+ outFileStream.close()
110
+ }
111
+ } else
112
+ mill.define.SystemStreams .withStreams(streams) {
113
+ thunk(streams)
114
+ }
115
+
82
116
def main0 (
83
117
args : Array [String ],
84
118
stateCache : RunnerState ,
@@ -92,10 +126,17 @@ object MillMain {
92
126
serverDir : os.Path
93
127
): (Boolean , RunnerState ) = {
94
128
95
- val streams = streams0
96
- mill.define.SystemStreams .withStreams(streams) {
97
- os.SubProcess .env.withValue(env) {
98
- MillCliConfigParser .parse(args) match {
129
+ os.SubProcess .env.withValue(env) {
130
+ val parserResult = MillCliConfigParser .parse(args)
131
+ // Detect when we're running in BSP mode as early as possible,
132
+ // and ensure we don't log to the default stdout or use the default
133
+ // stdin, meant to be used for BSP JSONRPC communication, where those
134
+ // logs would be lost.
135
+ // This is especially helpful if anything unexpectedly goes wrong
136
+ // early on, when developing on Mill or debugging things for example.
137
+ val bspMode = parserResult.toOption.exists(_.bsp.value)
138
+ withStreams(bspMode, streams0) { streams =>
139
+ parserResult match {
99
140
// Cannot parse args
100
141
case Result .Failure (msg) =>
101
142
streams.err.println(msg)
@@ -270,7 +311,7 @@ object MillMain {
270
311
requestedMetaLevel = config.metaLevel,
271
312
config.allowPositional.value,
272
313
systemExit = systemExit,
273
- streams0 = streams0 ,
314
+ streams0 = streams ,
274
315
selectiveExecution = config.watch.value,
275
316
scalaCompilerWorker = scalaCompilerWorker,
276
317
offline = config.offline.value
@@ -283,13 +324,14 @@ object MillMain {
283
324
if (bspMode) {
284
325
285
326
runBspSession(
327
+ streams0,
286
328
streams,
287
- ( prevRunnerState, splitStreams) =>
329
+ prevRunnerState =>
288
330
runMillBootstrap(
289
331
false ,
290
332
prevRunnerState,
291
333
Seq (" version" ),
292
- splitStreams
334
+ streams
293
335
).result
294
336
)
295
337
@@ -335,77 +377,61 @@ object MillMain {
335
377
}
336
378
}
337
379
380
+ /**
381
+ * Runs the BSP server, and exits when the server is done
382
+ *
383
+ * @param bspStreams Streams to use for BSP JSONRPC communication with the BSP client
384
+ * @param logStreams Streams to use for logging
385
+ * @param runMillBootstrap Load the Mill build, building / updating meta-builds along the way
386
+ */
338
387
def runBspSession (
339
- streams0 : SystemStreams ,
340
- runMillBootstrap : (Option [RunnerState ], SystemStreams ) => RunnerState
388
+ bspStreams : SystemStreams ,
389
+ logStreams : SystemStreams ,
390
+ runMillBootstrap : Option [RunnerState ] => RunnerState
341
391
): Result [BspServerResult ] = {
342
- val splitOut = new mill.internal.MultiStream (
343
- streams0.out,
344
- os.write.outputStream(
345
- WorkspaceRoot .workspaceRoot / OutFiles .out / " mill-bsp/out.log" ,
346
- createFolders = true
347
- )
348
- )
349
- val splitErr = new mill.internal.MultiStream (
350
- streams0.out,
351
- os.write.outputStream(
352
- WorkspaceRoot .workspaceRoot / OutFiles .out / " mill-bsp/err.log" ,
353
- createFolders = true
392
+ logStreams.err.println(" Trying to load BSP server..." )
393
+
394
+ val wsRoot = WorkspaceRoot .workspaceRoot
395
+ val logDir = wsRoot / OutFiles .out / " mill-bsp"
396
+ val bspServerHandleRes = {
397
+ os.makeDir.all(logDir)
398
+ mill.bsp.worker.BspWorkerImpl .startBspServer(
399
+ define.WorkspaceRoot .workspaceRoot,
400
+ bspStreams,
401
+ logDir,
402
+ true
354
403
)
355
- )
356
- val splitStreams = new SystemStreams (splitOut, splitErr, streams0.in)
357
-
358
- mill.define.SystemStreams .withStreams(splitStreams) {
359
- try {
360
- splitStreams.err.println(" Trying to load BSP server..." )
361
-
362
- val wsRoot = WorkspaceRoot .workspaceRoot
363
- val logDir = wsRoot / OutFiles .out / " mill-bsp"
364
- val bspServerHandleRes = {
365
- os.makeDir.all(logDir)
366
- mill.bsp.worker.BspWorkerImpl .startBspServer(
367
- define.WorkspaceRoot .workspaceRoot,
368
- splitStreams,
369
- logDir,
370
- true
371
- )
372
- }
373
-
374
- streams0.err.println(" BSP server started" )
375
-
376
- val runSessionRes = bspServerHandleRes.flatMap { bspServerHandle =>
377
- try {
378
- var repeatForBsp = true
379
- var bspRes : Option [Result [BspServerResult ]] = None
380
- var prevRunnerState : Option [RunnerState ] = None
381
- while (repeatForBsp) {
382
- repeatForBsp = false
383
- val runnerState = runMillBootstrap(prevRunnerState, splitStreams)
384
- val runSessionRes =
385
- bspServerHandle.runSession(runnerState.frames.flatMap(_.evaluator))
386
- prevRunnerState = Some (runnerState)
387
- repeatForBsp = runSessionRes == BspServerResult .ReloadWorkspace
388
- bspRes = Some (runSessionRes)
389
- streams0.err.println(s " BSP session returned with $runSessionRes" )
390
- }
404
+ }
391
405
392
- // should make the lsp4j-managed BSP server exit
393
- streams0.in.close()
406
+ logStreams.err.println(" BSP server started" )
394
407
395
- bspRes.get
396
- } finally bspServerHandle.close()
408
+ val runSessionRes = bspServerHandleRes.flatMap { bspServerHandle =>
409
+ try {
410
+ var repeatForBsp = true
411
+ var bspRes : Option [Result [BspServerResult ]] = None
412
+ var prevRunnerState : Option [RunnerState ] = None
413
+ while (repeatForBsp) {
414
+ repeatForBsp = false
415
+ val runnerState = runMillBootstrap(prevRunnerState)
416
+ val runSessionRes =
417
+ bspServerHandle.runSession(runnerState.frames.flatMap(_.evaluator))
418
+ prevRunnerState = Some (runnerState)
419
+ repeatForBsp = runSessionRes == BspServerResult .ReloadWorkspace
420
+ bspRes = Some (runSessionRes)
421
+ logStreams.err.println(s " BSP session returned with $runSessionRes" )
397
422
}
398
423
399
- splitErr.println(
400
- s " Exiting BSP runner loop. Stopping BSP server. Last result: $runSessionRes"
401
- )
402
- runSessionRes
403
- } finally {
424
+ // should make the lsp4j-managed BSP server exit
425
+ bspStreams.in.close()
404
426
405
- splitErr.close()
406
- splitOut.close()
407
- }
427
+ bspRes.get
428
+ } finally bspServerHandle.close()
408
429
}
430
+
431
+ logStreams.err.println(
432
+ s " Exiting BSP runner loop. Stopping BSP server. Last result: $runSessionRes"
433
+ )
434
+ runSessionRes
409
435
}
410
436
411
437
private [runner] def parseThreadCount (
0 commit comments