@@ -9,10 +9,10 @@ import mill.define
99import mill .define .WorkspaceRoot
1010import mill .util .BuildInfo
1111import mill .runner .meta .ScalaCompilerWorker
12- import mill .internal .{Colors , PromptLogger }
12+ import mill .internal .{Colors , MultiStream , PromptLogger }
1313import mill .server .Server
1414
15- import java .io .{PipedInputStream , PrintStream }
15+ import java .io .{InputStream , PipedInputStream , PrintStream }
1616import java .lang .reflect .InvocationTargetException
1717import java .util .Locale
1818import scala .collection .immutable
@@ -79,6 +79,40 @@ object MillMain {
7979
8080 lazy val maybeScalaCompilerWorker = ScalaCompilerWorker .bootstrapWorker()
8181
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+
82116 def main0 (
83117 args : Array [String ],
84118 stateCache : RunnerState ,
@@ -92,10 +126,17 @@ object MillMain {
92126 serverDir : os.Path
93127 ): (Boolean , RunnerState ) = {
94128
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 {
99140 // Cannot parse args
100141 case Result .Failure (msg) =>
101142 streams.err.println(msg)
@@ -270,7 +311,7 @@ object MillMain {
270311 requestedMetaLevel = config.metaLevel,
271312 config.allowPositional.value,
272313 systemExit = systemExit,
273- streams0 = streams0 ,
314+ streams0 = streams ,
274315 selectiveExecution = config.watch.value,
275316 scalaCompilerWorker = scalaCompilerWorker,
276317 offline = config.offline.value
@@ -283,13 +324,14 @@ object MillMain {
283324 if (bspMode) {
284325
285326 runBspSession(
327+ streams0,
286328 streams,
287- ( prevRunnerState, splitStreams) =>
329+ prevRunnerState =>
288330 runMillBootstrap(
289331 false ,
290332 prevRunnerState,
291333 Seq (" version" ),
292- splitStreams
334+ streams
293335 ).result
294336 )
295337
@@ -335,77 +377,61 @@ object MillMain {
335377 }
336378 }
337379
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+ */
338387 def runBspSession (
339- streams0 : SystemStreams ,
340- runMillBootstrap : (Option [RunnerState ], SystemStreams ) => RunnerState
388+ bspStreams : SystemStreams ,
389+ logStreams : SystemStreams ,
390+ runMillBootstrap : Option [RunnerState ] => RunnerState
341391 ): 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
354403 )
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+ }
391405
392- // should make the lsp4j-managed BSP server exit
393- streams0.in.close()
406+ logStreams.err.println(" BSP server started" )
394407
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" )
397422 }
398423
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()
404426
405- splitErr.close()
406- splitOut.close()
407- }
427+ bspRes.get
428+ } finally bspServerHandle.close()
408429 }
430+
431+ logStreams.err.println(
432+ s " Exiting BSP runner loop. Stopping BSP server. Last result: $runSessionRes"
433+ )
434+ runSessionRes
409435 }
410436
411437 private [runner] def parseThreadCount (
0 commit comments