diff --git a/core-it-suite/src/test/java/org/apache/maven/it/MavenITOutputCapabilitiesTest.java b/core-it-suite/src/test/java/org/apache/maven/it/MavenITOutputCapabilitiesTest.java
new file mode 100644
index 000000000..a76dfbb25
--- /dev/null
+++ b/core-it-suite/src/test/java/org/apache/maven/it/MavenITOutputCapabilitiesTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.it;
+
+import java.io.File;
+import java.util.Properties;
+
+import org.apache.maven.shared.verifier.Verifier;
+import org.apache.maven.shared.verifier.util.ResourceExtractor;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class MavenITOutputCapabilitiesTest extends AbstractMavenIntegrationTestCase {
+ MavenITOutputCapabilitiesTest() {
+ super("[3.10.0-SNAPSHOT,)");
+ }
+
+ @Test
+ void injectsCapabilitiesIntoLegacyPluginsInParallelReactor() throws Exception {
+ File directory = ResourceExtractor.simpleExtractResources(getClass(), "/output-capabilities");
+ Verifier plugin = newVerifier(new File(directory, "plugin").getPath(), "remote");
+ plugin.addCliArgument("install");
+ plugin.execute();
+ plugin.verifyErrorFreeLog();
+ Verifier legacyPlugin = newVerifier(new File(directory, "legacy-plugin").getPath(), "remote");
+ legacyPlugin.addCliArgument("install");
+ legacyPlugin.execute();
+ legacyPlugin.verifyErrorFreeLog();
+
+ for (String color : new String[] {"never", "always"}) {
+ Verifier consumer = newVerifier(new File(directory, "consumer").getPath());
+ consumer.addCliArgument("-T2");
+ consumer.addCliArgument("-l");
+ consumer.addCliArgument("build.log");
+ consumer.addCliArgument("-B");
+ consumer.addCliArgument("--color=" + color);
+ consumer.addCliArgument("validate");
+ consumer.execute();
+ consumer.verifyErrorFreeLog();
+ for (String prefix : new String[] {"", "child/"}) {
+ Properties properties = consumer.loadProperties(prefix + "target/output-capabilities.properties");
+ Properties legacy = consumer.loadProperties(prefix + "target/legacy-output-capabilities.properties");
+ assertEquals("true", legacy.getProperty("available"));
+ assertEquals(properties.getProperty("destination"), legacy.getProperty("destination"));
+ assertEquals(properties.getProperty("encoding"), legacy.getProperty("encoding"));
+ // Maven 3 opens -l output with the JVM default charset.
+ assertEquals("FILE", properties.getProperty("destination"));
+ assertEquals(properties.getProperty("defaultEncoding"), properties.getProperty("encoding"));
+ }
+ }
+ }
+}
diff --git a/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java b/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java
index 14da52359..c6db8066e 100644
--- a/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java
+++ b/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java
@@ -131,6 +131,7 @@ public TestSuiteOrdering() {
suite.addTestSuite(MavenITmng7228LeakyModelTest.class);
suite.addTestSuite(MavenITmng7819FileLockingWithSnapshotsTest.class);
suite.addTestSuite(MavenITmng7587Jsr330.class);
+ suite.addTestSuite(MavenITOutputCapabilitiesTest.class);
suite.addTestSuite(MavenITmng7038RootdirTest.class);
suite.addTestSuite(MavenITmng7697PomWithEmojiTest.class);
suite.addTestSuite(MavenITmng7737ProfileActivationTest.class);
diff --git a/core-it-suite/src/test/resources/output-capabilities/consumer/child/pom.xml b/core-it-suite/src/test/resources/output-capabilities/consumer/child/pom.xml
new file mode 100644
index 000000000..73671cbdb
--- /dev/null
+++ b/core-it-suite/src/test/resources/output-capabilities/consumer/child/pom.xml
@@ -0,0 +1,29 @@
+
+
+
+ 4.0.0
+
+ org.apache.maven.its.output
+ consumer
+ 1.0
+
+ child
+ pom
+
diff --git a/core-it-suite/src/test/resources/output-capabilities/consumer/pom.xml b/core-it-suite/src/test/resources/output-capabilities/consumer/pom.xml
new file mode 100644
index 000000000..70eceae68
--- /dev/null
+++ b/core-it-suite/src/test/resources/output-capabilities/consumer/pom.xml
@@ -0,0 +1,74 @@
+
+
+
+ 4.0.0
+ org.apache.maven.its.output
+ consumer
+ 1.0
+ pom
+
+ child
+
+
+
+
+ org.apache.maven.its.output
+ legacy-output-capabilities-maven-plugin
+ 1.0
+
+
+
+ report
+
+ validate
+
+
+
+
+
+
+
+ typed
+
+ true
+
+
+
+
+ org.apache.maven.its.output
+ output-capabilities-maven-plugin
+ 1.0
+
+
+
+ report
+
+ validate
+
+
+
+
+
+
+
+ legacy-only
+
+
+
diff --git a/core-it-suite/src/test/resources/output-capabilities/legacy-plugin/pom.xml b/core-it-suite/src/test/resources/output-capabilities/legacy-plugin/pom.xml
new file mode 100644
index 000000000..36f1cccb7
--- /dev/null
+++ b/core-it-suite/src/test/resources/output-capabilities/legacy-plugin/pom.xml
@@ -0,0 +1,63 @@
+
+
+
+ 4.0.0
+ org.apache.maven.its.output
+ legacy-output-capabilities-maven-plugin
+ 1.0
+ maven-plugin
+
+ 3.6.3
+ 8
+ UTF-8
+
+
+
+ org.apache.maven
+ maven-core
+ ${maven.version}
+ provided
+
+
+ org.apache.maven
+ maven-plugin-api
+ ${maven.version}
+ provided
+
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+ 3.15.2
+ provided
+
+
+
+
+
+ maven-compiler-plugin
+ 3.14.1
+
+
+ maven-plugin-plugin
+ 3.15.2
+
+
+
+
diff --git a/core-it-suite/src/test/resources/output-capabilities/legacy-plugin/src/main/java/org/apache/maven/its/output/LegacyOutputMojo.java b/core-it-suite/src/test/resources/output-capabilities/legacy-plugin/src/main/java/org/apache/maven/its/output/LegacyOutputMojo.java
new file mode 100644
index 000000000..e8df6b547
--- /dev/null
+++ b/core-it-suite/src/test/resources/output-capabilities/legacy-plugin/src/main/java/org/apache/maven/its/output/LegacyOutputMojo.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.its.output;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+
+/** This fixture must compile and run using only Maven 3.6.3 APIs. */
+@Mojo(name = "report", threadSafe = true)
+public class LegacyOutputMojo extends AbstractMojo {
+ @Parameter(defaultValue = "${session}", readonly = true)
+ private MavenSession session;
+
+ @Parameter(defaultValue = "${project.build.directory}/legacy-output-capabilities.properties", readonly = true)
+ private File output;
+
+ @Parameter(property = "capabilities.available", defaultValue = "true")
+ private boolean expectedAvailable;
+
+ @Override
+ public void execute() throws MojoExecutionException {
+ Object value = session.getRequest().getData().get("maven.logging.outputCapabilities");
+ boolean available = value instanceof Map;
+ if (expectedAvailable != available) {
+ throw new MojoExecutionException("Unexpected compatibility service availability: " + available);
+ }
+ Properties properties = new Properties();
+ properties.setProperty("available", Boolean.toString(available));
+ properties.setProperty("destination", "UNKNOWN");
+ properties.setProperty("encoding", "unknown");
+ properties.setProperty("defaultEncoding", Charset.defaultCharset().name());
+ if (available) {
+ Map, ?> snapshot = new HashMap<>((Map, ?>) value);
+ Object destination = snapshot.get("destination");
+ if (Arrays.asList("CONSOLE", "FILE", "REDIRECTED", "UNKNOWN").contains(destination)) {
+ properties.setProperty("destination", (String) destination);
+ }
+ Object encoding = snapshot.get("encoding");
+ if (encoding instanceof String) {
+ properties.setProperty("encoding", (String) encoding);
+ }
+ }
+ try {
+ Files.createDirectories(output.toPath().getParent());
+ try (OutputStream stream = Files.newOutputStream(output.toPath())) {
+ properties.store(stream, "Maven logging compatibility information");
+ }
+ } catch (IOException e) {
+ throw new MojoExecutionException("Unable to write capabilities", e);
+ }
+ getLog().info("legacy-output-capabilities-marker caf\u00e9 \u251c\u2500");
+ }
+}
diff --git a/core-it-suite/src/test/resources/output-capabilities/plugin/pom.xml b/core-it-suite/src/test/resources/output-capabilities/plugin/pom.xml
new file mode 100644
index 000000000..a7584a93e
--- /dev/null
+++ b/core-it-suite/src/test/resources/output-capabilities/plugin/pom.xml
@@ -0,0 +1,69 @@
+
+
+
+ 4.0.0
+ org.apache.maven.its.output
+ output-capabilities-maven-plugin
+ 1.0
+ maven-plugin
+
+ 3.10.0-SNAPSHOT
+ 8
+ UTF-8
+
+
+
+ org.apache.maven
+ maven-core
+ ${maven.version}
+ provided
+
+
+ org.apache.maven
+ maven-plugin-api
+ ${maven.version}
+ provided
+
+
+ javax.inject
+ javax.inject
+ 1
+ provided
+
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+ 3.15.2
+ provided
+
+
+
+
+
+ maven-compiler-plugin
+ 3.14.1
+
+
+ maven-plugin-plugin
+ 3.15.2
+
+
+
+
diff --git a/core-it-suite/src/test/resources/output-capabilities/plugin/src/main/java/org/apache/maven/its/output/OutputMojo.java b/core-it-suite/src/test/resources/output-capabilities/plugin/src/main/java/org/apache/maven/its/output/OutputMojo.java
new file mode 100644
index 000000000..8f1205158
--- /dev/null
+++ b/core-it-suite/src/test/resources/output-capabilities/plugin/src/main/java/org/apache/maven/its/output/OutputMojo.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.its.output;
+
+import javax.inject.Inject;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.util.Properties;
+
+import org.apache.maven.logging.OutputCapabilities;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+
+@Mojo(name = "report", threadSafe = true)
+public class OutputMojo extends AbstractMojo {
+ @Inject
+ private OutputCapabilities capabilities;
+
+ @Parameter(defaultValue = "${project.build.directory}/output-capabilities.properties", readonly = true)
+ private File output;
+
+ @Override
+ public void execute() throws MojoExecutionException {
+ Properties properties = new Properties();
+ properties.setProperty("destination", capabilities.getDestination().name());
+ properties.setProperty(
+ "encoding", capabilities.getEncoding().map(Charset::name).orElse("unknown"));
+ properties.setProperty("defaultEncoding", Charset.defaultCharset().name());
+ try {
+ Files.createDirectories(output.toPath().getParent());
+ try (OutputStream stream = Files.newOutputStream(output.toPath())) {
+ properties.store(stream, "Maven logging capabilities");
+ }
+ } catch (IOException e) {
+ throw new MojoExecutionException("Unable to write capabilities", e);
+ }
+ getLog().info("output-capabilities-marker caf\u00e9 \u251c\u2500");
+ }
+}