Repository: quarkusio/quarkus
Issue: Compilation errors are only logged in http response during hot reload

### Describe the bug

I often have the problem, that I think a hot reload was sucessful, and that I can continue testing.
The app successfully restarts, but has hidden compilation errors.

These compilation errors are only visible as the response to the http request.
But when I am testing using the frontend of the app, I will never see these compilation errors. I might not have the developer console of the browser open to see network requests.
They are not logged to dev ui, and not to the terminal running quarkus.

I then resort to stop quarkus, and either, dependending on what I am working on, do a full rebuild (mvn clean install), or a simple mvn quarkus:dev again.
Which will show me the errors in terminal

I am also not able to see errors from annotation processors because of this.

### Expected behavior

The errors are logged to the terminal / dev ui.

### Actual behavior

No errors visible on hot reload in the terminal / dev ui.

```
__  ____  __  _____   ___  __ ____  ______
 --/ __ \\/ / / / _ | / _ \\/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\\ \\
--\\___\\_\\____/_/ |_/_/|_/_/|_|\\____/___/
2021-10-08 07:55:40,641 INFO  [io.quarkus] (Quarkus Main Thread) yaml-hot-reload 1.0.0-SNAPSHOT on JVM (powered by Quarkus 2.3.0.Final) started in 0.802s. Listening on: http://localhos
t:8080
2021-10-08 07:55:40,642 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2021-10-08 07:55:40,643 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, config-yaml, resteasy-reactive, resteasy-reactive-jackson, smallrye-context-propagation, vert
x]
2021-10-08 07:55:40,645 INFO  [io.qua.dep.dev.RuntimeUpdatesProcessor] (vert.x-worker-thread-0) Live reload total time: 0.890s
```

### How to Reproduce?

1) Download the reproducer:
[yaml-hot-reload.zip](https://github.com/quarkusio/quarkus/files/7308919/yaml-hot-reload.zip) 
2) mvn quarkus:dev
3) Change anything in the source to cause compilation to fail
4) curl -v localhost:8080/hello. The app restarts sucessfuly.  No errors are visible in the terminal. The error is only visible as http response.
5) Fix the previous error.
6) curl -v localhost:8080/hello. The app restarts sucessfuly.
7) In ModelMapper.java, add `@Mapping(source = "name", target = "abc")` to the map method. This 
will cause mapstruct (annotation processor) to print out errors during compilation.
8) curl -v localhost:8080/hello. The app restarts sucessfuly. No errors are visible in the terminal. The error is only visible as http response.

### Output of `uname -a` or `ver`

MSYS_NT-10.0-19043 NANB7NLNVP2 3.1.7-340.x86_64 2021-03-26 22:17 UTC x86_64 Msys

### Output of `java -version`

openjdk version "17" 2021-09-14 OpenJDK Runtime Environment Temurin-17+35 (build 17+35) OpenJDK 64-Bit Server VM Temurin-17+35 (build 17+35, mixed mode, sharing)

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

2.3.0.Final

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

Apache Maven 3.8.3 (ff8e977a158738155dc465c6a97ffaf31982d739) Maven home: C:\\eclipse\\tools\\apache-maven Java version: 17, vendor: Eclipse Adoptium, runtime: C:\\eclipse\\tools\\java\\17 Default locale: de_DE, platform encoding: Cp1252 OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

### Additional information

_No response_

Changed files:
core/devmode-spi/src/main/java/io/quarkus/dev/console/BasicConsole.java
core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java
core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestConsoleHandler.java

diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java
index 1470e4ccc95..6220c8e3578 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java
@@ -64,6 +64,8 @@
 import io.quarkus.deployment.dev.testing.TestListener;
 import io.quarkus.deployment.dev.testing.TestSupport;
 import io.quarkus.deployment.util.FileUtil;
+import io.quarkus.dev.console.QuarkusConsole;
+import io.quarkus.dev.console.StatusLine;
 import io.quarkus.dev.spi.DevModeType;
 import io.quarkus.dev.spi.HotReplacementContext;
 import io.quarkus.dev.spi.HotReplacementSetup;
@@ -83,6 +85,7 @@ public class RuntimeUpdatesProcessor implements HotReplacementContext, Closeable
     private final QuarkusCompiler compiler;
     private final DevModeType devModeType;
     volatile Throwable compileProblem;
+    volatile Throwable testCompileProblem;
 
     private volatile Predicate<ClassInfo> disableInstrumentationForClassPredicate = new AlwaysFalsePredicate<>();
     private volatile Predicate<Index> disableInstrumentationForIndexPredicate = new AlwaysFalsePredicate<>();
@@ -124,6 +127,7 @@ public class RuntimeUpdatesProcessor implements HotReplacementContext, Closeable
 
     private WatchServiceFileSystemWatcher testClassChangeWatcher;
     private Timer testClassChangeTimer;
+    volatile StatusLine compileOutput;
 
     public RuntimeUpdatesProcessor(Path applicationRoot, DevModeContext context, QuarkusCompiler compiler,
             DevModeType devModeType, BiConsumer<Set<String>, ClassScanResult> restartCallback,
@@ -293,10 +297,11 @@ private void periodicTestCompile() {
         TestScanningLock.lockForTests();
         try {
             ClassScanResult changedTestClassResult = compileTestClasses();
-            ClassScanResult changedApp = checkForChangedClasses(compiler, DevModeContext.ModuleInfo::getMain, false, test);
+            ClassScanResult changedApp = checkForChangedClasses(compiler, DevModeContext.ModuleInfo::getMain, false, test,
+                    true);
             if (changedApp.compilationHappened) {
-                if (compileProblem != null) {
-                    testSupport.testCompileFailed(compileProblem);
+                if (testCompileProblem != null) {
+                    testSupport.testCompileFailed(testCompileProblem);
                 } else {
                     testSupport.testCompileSucceeded();
                 }
@@ -308,11 +313,11 @@ private void periodicTestCompile() {
 
             ClassScanResult merged = ClassScanResult.merge(changedTestClassResult, changedApp);
             if (configFileRestartNeeded) {
-                if (compileProblem == null) {
+                if (testCompileProblem == null) {
                     testSupport.runTests(null);
                 }
             } else if (merged.isChanged()) {
-                if (compileProblem == null) {
+                if (testCompileProblem == null) {
                     testSupport.runTests(merged);
                 }
             }
@@ -327,10 +332,9 @@ private ClassScanResult compileTestClasses() {
         ClassScanResult changedTestClassResult = new ClassScanResult();
         try {
             changedTestClassResult = checkForChangedClasses(testCompiler,
-                    m -> m.getTest().orElse(DevModeContext.EMPTY_COMPILATION_UNIT), false, test);
+                    m -> m.getTest().orElse(DevModeContext.EMPTY_COMPILATION_UNIT), false, test, true);
             if (compileProblem != null) {
                 testSupport.testCompileFailed(compileProblem);
-                compileProblem = null; //we don't want to block the app over a test problem
             } else {
                 if (changedTestClassResult.isChanged()) {
                     testSupport.testCompileSucceeded();
@@ -368,6 +372,18 @@ public Throwable getDeploymentProblem() {
     @Override
     public void setRemoteProblem(Throwable throwable) {
         compileProblem = throwable;
+        getCompileOutput().setMessage(throwable.getMessage());
+    }
+
+    private StatusLine getCompileOutput() {
+        if (compileOutput == null) {
+            synchronized (this) {
+                if (compileOutput == null) {
+                    compileOutput = QuarkusConsole.INSTANCE.registerStatusLine(QuarkusConsole.COMPILE_ERROR);
+                }
+            }
+        }
+        return compileOutput;
     }
 
     @Override
@@ -417,7 +433,7 @@ public boolean doScan(boolean userInitiated, boolean forceRestart) {
             }
 
             ClassScanResult changedClassResults = checkForChangedClasses(compiler, DevModeContext.ModuleInfo::getMain, false,
-                    main);
+                    main, false);
             Set<String> filesChanged = checkForFileChange(DevModeContext.ModuleInfo::getMain, main);
 
             boolean configFileRestartNeeded = forceRestart || filesChanged.stream().map(main.watchedFilePaths::get)
@@ -477,6 +493,9 @@ public boolean doScan(boolean userInitiated, boolean forceRestart) {
                     }
                 }
             }
+            if (compileProblem != null) {
+                return false;
+            }
 
             //if there is a deployment problem we always restart on scan
             //this is because we can't setup the config file watches
@@ -582,7 +601,8 @@ public Set<String> syncState(Map<String, String> fileHashes) {
     }
 
     ClassScanResult checkForChangedClasses(boolean firstScan) {
-        ClassScanResult classScanResult = checkForChangedClasses(compiler, DevModeContext.ModuleInfo::getMain, firstScan, main);
+        ClassScanResult classScanResult = checkForChangedClasses(compiler, DevModeContext.ModuleInfo::getMain, firstScan, main,
+                false);
         if (firstScan) {
             test.merge(main);
         }
@@ -595,7 +615,7 @@ ClassScanResult checkForChangedTestClasses(boolean firstScan) {
         }
         ClassScanResult ret = checkForChangedClasses(testSupport.getCompiler(),
                 s -> s.getTest().orElse(DevModeContext.EMPTY_COMPILATION_UNIT), firstScan,
-                test);
+                test, true);
         if (firstScan) {
             startTestScanningTimer();
         }
@@ -611,7 +631,7 @@ ClassScanResult checkForChangedTestClasses(boolean firstScan) {
      */
     ClassScanResult checkForChangedClasses(QuarkusCompiler compiler,
             Function<DevModeContext.ModuleInfo, DevModeContext.CompilationUnit> cuf, boolean firstScan,
-            TimestampSet timestampSet) {
+            TimestampSet timestampSet, boolean compilingTests) {
         ClassScanResult classScanResult = new ClassScanResult();
         boolean ignoreFirstScanChanges = firstScan;
 
@@ -670,8 +690,17 @@ && sourceFileWasRecentModified(p, ignoreFirstScanChanges, firstScan))
                             compiler.compile(sourcePath.toString(), changedSourceFiles.stream()
                                     .collect(groupingBy(this::getFileExtension, Collectors.toSet())));
                             compileProblem = null;
+                            if (compilingTests) {
+                                testCompileProblem = null;
+                            }
+                            getCompileOutput().setMessage(null);
                         } catch (Exception e) {
-                            compileProblem = e;
+                            if (compilingTests) {
+                                testCompileProblem = e;
+                            } else {
+                                compileProblem = e;
+                            }
+                            getCompileOutput().setMessage(e.getMessage());
                             return classScanResult;
                         }
                         boolean timestampsChanged = false;
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestConsoleHandler.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestConsoleHandler.java
index 00d6f6c75fd..764b66944c3 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestConsoleHandler.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestConsoleHandler.java
@@ -49,7 +49,6 @@ public class TestConsoleHandler implements TestListener {
     private volatile ConsoleStateManager.ConsoleContext consoleContext;
     private volatile StatusLine resultsOutput;
     private volatile StatusLine testsStatusOutput;
-    private volatile StatusLine testsCompileOutput;
 
     public TestConsoleHandler(DevModeType devModeType) {
         this.devModeType = devModeType;
@@ -79,7 +78,6 @@ public void listenerRegistered(TestController testController) {
         this.consoleContext = ConsoleStateManager.INSTANCE.createContext("Continuous Testing");
         this.resultsOutput = QuarkusConsole.INSTANCE.registerStatusLine(QuarkusConsole.TEST_RESULTS);
         this.testsStatusOutput = QuarkusConsole.INSTANCE.registerStatusLine(QuarkusConsole.TEST_STATUS);
-        this.testsCompileOutput = QuarkusConsole.INSTANCE.registerStatusLine(QuarkusConsole.COMPILE_ERROR);
         setupPausedConsole();
     }
 
@@ -145,16 +143,6 @@ public void testsDisabled() {
         setupPausedConsole();
     }
 
-    @Override
-    public void testCompileFailed(String message) {
-        testsCompileOutput.setMessage(message);
-    }
-
-    @Override
-    public void testCompileSucceeded() {
-        testsCompileOutput.setMessage(null);
-    }
-
     @Override
     public void testRunStarted(Consumer<TestRunListener> listenerConsumer) {
 
diff --git a/core/devmode-spi/src/main/java/io/quarkus/dev/console/BasicConsole.java b/core/devmode-spi/src/main/java/io/quarkus/dev/console/BasicConsole.java
index 6d82fd3c2b8..8c146c4ff21 100644
--- a/core/devmode-spi/src/main/java/io/quarkus/dev/console/BasicConsole.java
+++ b/core/devmode-spi/src/main/java/io/quarkus/dev/console/BasicConsole.java
@@ -114,6 +114,7 @@ public void setMessage(String message) {
                     return;
                 }
                 if (message == null) {
+                    old = null;
                     return;
                 }
                 if (message.equals(old)) {