diff --git a/modules/build/src/main/scala/scala/build/Build.scala b/modules/build/src/main/scala/scala/build/Build.scala index d22552c71c..1e5ff88fa2 100644 --- a/modules/build/src/main/scala/scala/build/Build.scala +++ b/modules/build/src/main/scala/scala/build/Build.scala @@ -683,12 +683,12 @@ object Build { val threads = BuildThreads.create() val classesDir0 = classesRootDir(inputs.workspace, inputs.projectName) - def info: Either[BuildException, (ScalaCompiler, Option[ScalaCompiler], CrossSources, Inputs)] = + lazy val compilers: Either[BuildException, (ScalaCompiler, Option[ScalaCompiler])] = either { val (crossSources: CrossSources, inputs0: Inputs) = value(allInputs(inputs, options, logger)) val sharedOptions = crossSources.sharedOptions(options) - val compiler: ScalaCompiler = value { + val compiler = value { compilerMaker.create( inputs0.workspace / Constants.workspaceDirName, classesDir0, @@ -697,13 +697,21 @@ object Build { sharedOptions ) } - val docCompilerOpt: Option[ScalaCompiler] = docCompilerMakerOpt.map(_.create( + val docCompilerOpt = docCompilerMakerOpt.map(_.create( inputs0.workspace / Constants.workspaceDirName, classesDir0, buildClient, logger, sharedOptions )).map(value) + compiler -> docCompilerOpt + } + + def info: Either[BuildException, (ScalaCompiler, Option[ScalaCompiler], CrossSources, Inputs)] = + either { + val (crossSources: CrossSources, inputs0: Inputs) = + value(allInputs(inputs, options, logger)) + val (compiler, docCompilerOpt) = value(compilers) (compiler, docCompilerOpt, crossSources, inputs0) } diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunWithWatchTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunWithWatchTestDefinitions.scala index 5034c06932..853d9032ec 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunWithWatchTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunWithWatchTestDefinitions.scala @@ -338,4 +338,43 @@ trait RunWithWatchTestDefinitions { _: RunTestDefinitions => } } } + + def testRepeatedRerunsWithWatch(): Unit = { + def expectedMessage(number: Int) = s"Hello $number" + + def content(number: Int) = + s"@main def main(): Unit = println(\"${expectedMessage(number)}\")" + + TestUtil.retryOnCi() { + val inputPath = os.rel / "example.scala" + TestInputs(inputPath -> content(0)).fromRoot { root => + os.proc(TestUtil.cli, "--power", "bloop", "exit").call(cwd = root) + TestUtil.withProcessWatching( + proc = os.proc(TestUtil.cli, ".", "--watch", extraOptions) + .spawn(cwd = root, stderr = os.Pipe) + ) { (proc, timeout, ec) => + for (num <- 1 to 10) { + val output = TestUtil.readLine(proc.stdout, ec, timeout) + expect(output == expectedMessage(num - 1)) + proc.printStderrUntilRerun(timeout)(ec) + Thread.sleep(200L) + if (num < 10) { + val newContent = content(num) + os.write.over(root / inputPath, newContent) + } + } + } + } + } + } + + if (actualScalaVersion.startsWith("3") && Properties.isMac && TestUtil.isCI) + // TODO make this pass reliably on Mac CI (seems to work fine locally) + test("watch mode doesnt hang on Bloop when rebuilding repeatedly".flaky) { + testRepeatedRerunsWithWatch() + } + else if (actualScalaVersion.startsWith("3")) + test("watch mode doesnt hang on Bloop when rebuilding repeatedly") { + testRepeatedRerunsWithWatch() + } }