diff --git a/frontend/src/main/scala/bloop/cli/ExitStatus.scala b/frontend/src/main/scala/bloop/cli/ExitStatus.scala index 7846da97f0..4ec9541679 100644 --- a/frontend/src/main/scala/bloop/cli/ExitStatus.scala +++ b/frontend/src/main/scala/bloop/cli/ExitStatus.scala @@ -29,7 +29,7 @@ object ExitStatus { } } - val Ok, UnexpectedError, ParseError, InvalidCommandLineOption: ExitStatus = + val Ok, UnexpectedError, ParseError, InvalidCommandLineOption, CompilationError, TestExecutionError, RunError: ExitStatus = generateExitStatus def apply(code: Int): ExitStatus = { diff --git a/frontend/src/main/scala/bloop/engine/Interpreter.scala b/frontend/src/main/scala/bloop/engine/Interpreter.scala index 8ee7898801..0ebd4e73c3 100644 --- a/frontend/src/main/scala/bloop/engine/Interpreter.scala +++ b/frontend/src/main/scala/bloop/engine/Interpreter.scala @@ -101,9 +101,8 @@ object Interpreter { state.build.getProjectFor(cmd.project) match { case Some(project) => - def doCompile(state: State): Task[State] = { + def doCompile(state: State): Task[State] = Tasks.compile(state, project, reporterConfig).map(_.mergeStatus(ExitStatus.Ok)) - } val initialState = { if (cmd.incremental) Task(state) @@ -112,6 +111,7 @@ object Interpreter { Tasks.clean(state, state.build.projects, true) } } + initialState.flatMap { state => if (!cmd.watch) doCompile(state) else watch(project, state, doCompile _) @@ -139,15 +139,31 @@ object Interpreter { state.mergeStatus(ExitStatus.Ok) } + private def compileAnd( + state: State, + project: Project, + reporterConfig: ReporterConfig, + excludeRoot: Boolean + )(next: State => Task[State]): Task[State] = { + Tasks.compile(state, project, reporterConfig, excludeRoot).flatMap { compiled => + if (compiled.status != ExitStatus.CompilationError) next(compiled) + else { + Task.now { + compiled.logger.debug(s"Failed compilation for ${project}. Skipping tests.") + compiled + } + } + } + } + private def console(cmd: Commands.Console, state: State): Task[State] = { val reporterConfig = ReporterConfig.toFormat(cmd.reporter) state.build.getProjectFor(cmd.project) match { case Some(project) => - for { - compiled <- Tasks.compile(state, project, reporterConfig, cmd.excludeRoot) - result <- Tasks.console(compiled, project, reporterConfig, cmd.excludeRoot) - } yield result.mergeStatus(ExitStatus.Ok) + compileAnd(state, project, reporterConfig, cmd.excludeRoot) { state => + Tasks.console(state, project, reporterConfig, cmd.excludeRoot) + } case None => Task(reportMissing(cmd.project :: Nil, state)) } @@ -161,10 +177,10 @@ object Interpreter { def doTest(state: State): Task[State] = { val testFilter = TestInternals.parseFilters(cmd.filter) val cwd = cmd.cliOptions.common.workingPath - for { - compiled <- Tasks.compile(state, project, reporterConfig, excludeRoot = false) - result <- Tasks.test(compiled, project, cwd, cmd.isolated, testFilter) - } yield result + + compileAnd(state, project, reporterConfig, excludeRoot = false) { state => + Tasks.test(state, project, cwd, cmd.isolated, testFilter) + } } if (cmd.watch) watch(project, state, doTest _) else doTest(state) @@ -222,15 +238,15 @@ object Interpreter { } } } + def doRun(state: State): Task[State] = { - Tasks.compile(state, project, reporter, excludeRoot = false).flatMap { compiled => - getMainClass(compiled) match { - case None => - Task(compiled.mergeStatus(ExitStatus.UnexpectedError)) + compileAnd(state, project, reporter, excludeRoot = false) { state => + getMainClass(state) match { + case None => Task(state.mergeStatus(ExitStatus.RunError)) case Some(main) => val args = cmd.args.toArray val cwd = cmd.cliOptions.common.workingPath - Tasks.run(compiled, project, cwd, main, args) + Tasks.run(state, project, cwd, main, args) } } } diff --git a/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala b/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala index bec5d200e4..c6b506163b 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala @@ -94,9 +94,12 @@ object Tasks { val results = Dag.dfs(results0) val failures = Compilation.Result.failedProjects(results).distinct val successes = Compilation.Result.successfulProjects(results) - val newCache = state.results.addResults(successes) - failures.foreach(p => logger.error(s"'${p.name}' failed to compile.")) - state.copy(results = newCache) + val newState = state.copy(results = state.results.addResults(successes)) + if (failures.isEmpty) newState + else { + failures.foreach(p => logger.error(s"'${p.name}' failed to compile.")) + newState.copy(status = ExitStatus.CompilationError) + } } }