Merge pull request #16947 from gradle/wolfs/tc/retry-flaky-test

Add a build to rerun flaky tests when desired
diff --git a/subprojects/architecture-test/src/changes/accepted-public-api-changes.json b/subprojects/architecture-test/src/changes/accepted-public-api-changes.json
index fe2f898..1efc41e 100644
--- a/subprojects/architecture-test/src/changes/accepted-public-api-changes.json
+++ b/subprojects/architecture-test/src/changes/accepted-public-api-changes.json
@@ -507,6 +507,12 @@
             "changes": [
                 "Constructor has been removed"
             ]
+        },
+        {
+            "type": "org.gradle.testing.jacoco.tasks.JacocoCoverageVerification",
+            "member": "Method org.gradle.testing.jacoco.tasks.JacocoCoverageVerification.getDummyOutputFile()",
+            "acceptation": "This method is for internal use only",
+            "changes": []
         }
     ]
 }
diff --git a/subprojects/build-events/src/main/java/org/gradle/internal/build/event/BuildEventSubscriptions.java b/subprojects/build-events/src/main/java/org/gradle/internal/build/event/BuildEventSubscriptions.java
index da61a09..e1fc929 100644
--- a/subprojects/build-events/src/main/java/org/gradle/internal/build/event/BuildEventSubscriptions.java
+++ b/subprojects/build-events/src/main/java/org/gradle/internal/build/event/BuildEventSubscriptions.java
@@ -16,23 +16,26 @@
 
 package org.gradle.internal.build.event;
 
+import com.google.common.collect.ImmutableSet;
 import org.gradle.tooling.events.OperationType;
 
-import java.io.Serializable;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.EnumSet;
 import java.util.Set;
 
 /**
  * Provides information about what events the build client is interested in.
  */
-public class BuildEventSubscriptions implements Serializable {
+public class BuildEventSubscriptions {
 
     private final Set<OperationType> operationTypes;
 
     public BuildEventSubscriptions(Set<OperationType> operationTypes) {
-        this.operationTypes = EnumSet.copyOf(operationTypes);
+        this.operationTypes = ImmutableSet.copyOf(operationTypes);
+    }
+
+    public Set<OperationType> getOperationTypes() {
+        return operationTypes;
     }
 
     public boolean isRequested(OperationType workItem) {
diff --git a/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/BooleanBuildOption.java b/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/BooleanBuildOption.java
index bceed0d..25fd78f 100644
--- a/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/BooleanBuildOption.java
+++ b/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/BooleanBuildOption.java
@@ -43,7 +43,7 @@
         String value = properties.get(gradleProperty);
 
         if (value != null) {
-            applyTo(isTrue(properties.get(gradleProperty)), settings, Origin.forGradleProperty(gradleProperty));
+            applyTo(isTrue(value), settings, Origin.forGradleProperty(gradleProperty));
         }
     }
 
diff --git a/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/BuildOption.java b/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/BuildOption.java
index 3a7ea64..a33ab86 100644
--- a/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/BuildOption.java
+++ b/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/BuildOption.java
@@ -39,4 +39,44 @@
 
     void applyFromCommandLine(ParsedCommandLine options, T settings);
 
+    abstract class Value<T> {
+        public abstract boolean isExplicit();
+
+        public abstract T get();
+
+        /**
+         * Creates the default value for an option.
+         */
+        public static <T> Value<T> defaultValue(final T value) {
+            return new Value<T>() {
+                @Override
+                public boolean isExplicit() {
+                    return false;
+                }
+
+                @Override
+                public T get() {
+                    return value;
+                }
+            };
+        }
+
+        /**
+         * Creates an explicit value for an option.
+         */
+        public static <T> Value<T> value(final T value) {
+            return new Value<T>() {
+                @Override
+                public boolean isExplicit() {
+                    return true;
+                }
+
+                @Override
+                public T get() {
+                    return value;
+                }
+            };
+        }
+    }
+
 }
diff --git a/subprojects/composite-builds/build.gradle.kts b/subprojects/composite-builds/build.gradle.kts
index e25dd08..da0dc89 100644
--- a/subprojects/composite-builds/build.gradle.kts
+++ b/subprojects/composite-builds/build.gradle.kts
@@ -16,6 +16,7 @@
     implementation(libs.guava)
 
     testImplementation(project(":file-watching"))
+    testImplementation(project(":build-option"))
     testImplementation(testFixtures(project(":dependency-management")))
 
     integTestImplementation(project(":build-option"))
diff --git a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/RootOfNestedBuildTree.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/RootOfNestedBuildTree.java
index da533c6..a41c659 100644
--- a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/RootOfNestedBuildTree.java
+++ b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/RootOfNestedBuildTree.java
@@ -19,26 +19,26 @@
 import org.gradle.api.artifacts.component.BuildIdentifier;
 import org.gradle.api.initialization.Settings;
 import org.gradle.api.internal.BuildDefinition;
-import org.gradle.api.internal.BuildType;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.SettingsInternal;
 import org.gradle.api.internal.StartParameterInternal;
 import org.gradle.initialization.BuildCancellationToken;
-import org.gradle.internal.build.BuildModelControllerServices;
 import org.gradle.initialization.BuildRequestMetaData;
 import org.gradle.initialization.DefaultBuildRequestMetaData;
-import org.gradle.internal.build.BuildLifecycleControllerFactory;
 import org.gradle.initialization.NoOpBuildEventConsumer;
 import org.gradle.initialization.RunNestedBuildBuildOperationType;
 import org.gradle.initialization.layout.BuildLayout;
 import org.gradle.internal.InternalBuildAdapter;
 import org.gradle.internal.build.AbstractBuildState;
 import org.gradle.internal.build.BuildLifecycleController;
+import org.gradle.internal.build.BuildLifecycleControllerFactory;
+import org.gradle.internal.build.BuildModelControllerServices;
 import org.gradle.internal.build.BuildState;
 import org.gradle.internal.build.BuildStateRegistry;
 import org.gradle.internal.build.NestedRootBuild;
 import org.gradle.internal.buildtree.BuildTreeController;
 import org.gradle.internal.buildtree.BuildTreeLifecycleController;
+import org.gradle.internal.buildtree.BuildTreeModelControllerServices;
 import org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.concurrent.CompositeStoppable;
@@ -81,9 +81,8 @@
         StartParameterInternal startParameter = buildDefinition.getStartParameter();
         BuildRequestMetaData buildRequestMetaData = new DefaultBuildRequestMetaData(Time.currentTimeMillis());
         session = new BuildSessionController(userHomeDirServiceRegistry, crossBuildSessionState, startParameter, buildRequestMetaData, ClassPath.EMPTY, buildCancellationToken, buildRequestMetaData.getClient(), new NoOpBuildEventConsumer());
-        // Configuration cache is not supported for nested build trees
-        startParameter.setConfigurationCache(false);
-        buildTree = new BuildTreeController(session.getServices(), BuildType.TASKS);
+        BuildTreeModelControllerServices.Supplier modelServices = session.getServices().get(BuildTreeModelControllerServices.class).servicesForNestedBuildTree(startParameter);
+        buildTree = new BuildTreeController(session.getServices(), modelServices);
         // Create the controller using the services of the nested tree
         BuildLifecycleControllerFactory buildLifecycleControllerFactory = buildTree.getServices().get(BuildLifecycleControllerFactory.class);
         BuildScopeServices buildScopeServices = new BuildScopeServices(buildTree.getServices());
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/AbstractConfigurationCacheIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/AbstractConfigurationCacheIntegrationTest.groovy
index d2d8cc6..42f78f8 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/AbstractConfigurationCacheIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/AbstractConfigurationCacheIntegrationTest.groovy
@@ -16,15 +16,15 @@
 
 package org.gradle.configurationcache
 
+import org.gradle.configurationcache.fixtures.AbstractOptInFeatureIntegrationTest
 import org.gradle.initialization.StartParameterBuildOptions.ConfigurationCacheMaxProblemsOption
 import org.gradle.initialization.StartParameterBuildOptions.ConfigurationCacheOption
 import org.gradle.initialization.StartParameterBuildOptions.ConfigurationCacheProblemsOption
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
 import org.gradle.integtests.fixtures.configurationcache.ConfigurationCacheProblemsFixture
 import org.intellij.lang.annotations.Language
 
-class AbstractConfigurationCacheIntegrationTest extends AbstractIntegrationSpec {
+abstract class AbstractConfigurationCacheIntegrationTest extends AbstractOptInFeatureIntegrationTest {
 
     static final String ENABLE_CLI_OPT = "--${ConfigurationCacheOption.LONG_OPTION}"
     static final String ENABLE_GRADLE_PROP = "${ConfigurationCacheOption.PROPERTY_NAME}=true"
@@ -57,14 +57,17 @@
         buildKotlinFile << script
     }
 
+    @Override
     void configurationCacheRun(String... tasks) {
         run(ENABLE_CLI_OPT, *tasks)
     }
 
+    @Override
     void configurationCacheRunLenient(String... tasks) {
         run(ENABLE_CLI_OPT, WARN_PROBLEMS_CLI_OPT, *tasks)
     }
 
+    @Override
     void configurationCacheFails(String... tasks) {
         fails(ENABLE_CLI_OPT, *tasks)
     }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheEnablementIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheEnablementIntegrationTest.groovy
index e02a76c..6acac45 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheEnablementIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheEnablementIntegrationTest.groovy
@@ -123,12 +123,7 @@
     }
 
     private void outputContainsIncubatingFeatureUsage() {
-        outputContains("Configuration cache is an incubating feature.")
-    }
-
-    private void expectDeprecatedProperty(String name, String value) {
-        executer.beforeExecute {
-            expectDeprecationWarning("Property '$name' with value '$value' has been deprecated. This is scheduled to be removed in Gradle 7.0.")
-        }
+        outputContains(CONFIGURATION_CACHE_MESSAGE)
+        outputDoesNotContain(ISOLATED_PROJECTS_MESSAGE)
     }
 }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/fixtures/AbstractOptInFeatureIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/fixtures/AbstractOptInFeatureIntegrationTest.groovy
new file mode 100644
index 0000000..6bc8f88
--- /dev/null
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/fixtures/AbstractOptInFeatureIntegrationTest.groovy
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed 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.gradle.configurationcache.fixtures
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+abstract class AbstractOptInFeatureIntegrationTest extends AbstractIntegrationSpec {
+    static final String CONFIGURATION_CACHE_MESSAGE = "Configuration cache is an incubating feature."
+    static final String ISOLATED_PROJECTS_MESSAGE = "Isolated projects is an incubating feature."
+
+    abstract void configurationCacheRun(String... tasks)
+
+    abstract void configurationCacheRunLenient(String... tasks)
+
+    abstract void configurationCacheFails(String... tasks)
+}
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovyBuildScriptPluginIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovyBuildScriptPluginIntegrationTest.groovy
index b9142cf..c65f8fd 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovyBuildScriptPluginIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovyBuildScriptPluginIntegrationTest.groovy
@@ -16,8 +16,6 @@
 
 package org.gradle.configurationcache.inputs.undeclared
 
-import spock.lang.Ignore
-
 class UndeclaredBuildInputsDynamicGroovyBuildScriptPluginIntegrationTest extends AbstractUndeclaredBuildInputsIntegrationTest implements GroovyPluginImplementation {
     @Override
     String getLocation() {
@@ -31,14 +29,4 @@
             apply plugin: SneakyPlugin
         """
     }
-
-    @Ignore
-    def "can reference static methods via instance variables"() {
-        expect: false
-    }
-
-    @Ignore
-    def "can reference methods from groovy closure"() {
-        expect: false
-    }
 }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovyBuildSrcIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovyBuildSrcIntegrationTest.groovy
index f94ecd0..495de6c 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovyBuildSrcIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovyBuildSrcIntegrationTest.groovy
@@ -16,8 +16,6 @@
 
 package org.gradle.configurationcache.inputs.undeclared
 
-import spock.lang.Ignore
-
 class UndeclaredBuildInputsDynamicGroovyBuildSrcIntegrationTest extends AbstractUndeclaredBuildInputsIntegrationTest implements GroovyPluginImplementation {
     @Override
     String getLocation() {
@@ -31,14 +29,4 @@
             apply plugin: SneakyPlugin
         """
     }
-
-    @Ignore
-    def "can reference static methods via instance variables"() {
-        expect: false
-    }
-
-    @Ignore
-    def "can reference methods from groovy closure"() {
-        expect: false
-    }
 }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsKotlinBuildScriptPluginIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsKotlinBuildScriptPluginIntegrationTest.groovy
index 4d471a3..a253127 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsKotlinBuildScriptPluginIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsKotlinBuildScriptPluginIntegrationTest.groovy
@@ -16,8 +16,6 @@
 
 package org.gradle.configurationcache.inputs.undeclared
 
-import spock.lang.Ignore
-
 class UndeclaredBuildInputsKotlinBuildScriptPluginIntegrationTest extends AbstractUndeclaredBuildInputsIntegrationTest implements KotlinPluginImplementation {
     @Override
     String getLocation() {
@@ -31,9 +29,4 @@
             plugins.apply(SneakyPlugin::class.java)
         """
     }
-
-    @Ignore
-    def "can reference methods from kotlin function"() {
-        expect: false
-    }
 }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsKotlinBuildSrcIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsKotlinBuildSrcIntegrationTest.groovy
index b7fc189..324ad24 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsKotlinBuildSrcIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsKotlinBuildSrcIntegrationTest.groovy
@@ -16,9 +16,7 @@
 
 package org.gradle.configurationcache.inputs.undeclared
 
-
 import org.gradle.integtests.fixtures.KotlinDslTestUtil
-import spock.lang.Ignore
 
 class UndeclaredBuildInputsKotlinBuildSrcIntegrationTest extends AbstractUndeclaredBuildInputsIntegrationTest implements KotlinPluginImplementation {
     @Override
@@ -34,9 +32,4 @@
             apply plugin: SneakyPlugin
         """
     }
-
-    @Ignore
-    def "can reference methods from kotlin function"() {
-        expect: false
-    }
 }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsStaticGroovyBuildSrcIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsStaticGroovyBuildSrcIntegrationTest.groovy
index 6535e71..cd377ea 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsStaticGroovyBuildSrcIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsStaticGroovyBuildSrcIntegrationTest.groovy
@@ -16,7 +16,7 @@
 
 package org.gradle.configurationcache.inputs.undeclared
 
-import spock.lang.Ignore
+
 import spock.lang.Issue
 
 @Issue("https://github.com/gradle/gradle-private/issues/3252")
@@ -33,14 +33,4 @@
             apply plugin: SneakyPlugin
         """
     }
-
-    @Ignore
-    def "can reference static methods via instance variables"() {
-        expect: false
-    }
-
-    @Ignore
-    def "can reference methods from groovy closure"() {
-        expect: false
-    }
 }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/AbstractIsolatedProjectsIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/AbstractIsolatedProjectsIntegrationTest.groovy
new file mode 100644
index 0000000..140dc60
--- /dev/null
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/AbstractIsolatedProjectsIntegrationTest.groovy
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed 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.gradle.configurationcache.isolated
+
+import org.gradle.configurationcache.fixtures.AbstractOptInFeatureIntegrationTest
+
+abstract class AbstractIsolatedProjectsIntegrationTest extends AbstractOptInFeatureIntegrationTest {
+    public static final String ENABLE_CLI = "-Dorg.gradle.unsafe.isolated-projects=true"
+
+    @Override
+    void configurationCacheRun(String... tasks) {
+        run(ENABLE_CLI, *tasks)
+    }
+
+    @Override
+    void configurationCacheRunLenient(String... tasks) {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
+    void configurationCacheFails(String... tasks) {
+        fails(ENABLE_CLI, *tasks)
+    }
+}
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsIntegrationTest.groovy
new file mode 100644
index 0000000..84bb7bf
--- /dev/null
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsIntegrationTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed 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.gradle.configurationcache.isolated
+
+class IsolatedProjectsIntegrationTest extends AbstractIsolatedProjectsIntegrationTest {
+    def "option also enables configuration cache"() {
+        settingsFile << """
+            println "configuring settings"
+        """
+        buildFile """
+            println "configuring project"
+            task thing { }
+        """
+
+        def configurationCache = newConfigurationCacheFixture()
+
+        when:
+        configurationCacheRun("thing")
+
+        then:
+        configurationCache.assertStateStored()
+        outputContains(ISOLATED_PROJECTS_MESSAGE)
+        outputDoesNotContain(CONFIGURATION_CACHE_MESSAGE)
+        outputContains("configuring settings")
+        outputContains("configuring project")
+
+        when:
+        configurationCacheRun("thing")
+
+        then:
+        configurationCache.assertStateLoaded()
+        outputContains(ISOLATED_PROJECTS_MESSAGE)
+        outputDoesNotContain(CONFIGURATION_CACHE_MESSAGE)
+        outputDoesNotContain("configuring settings")
+        outputDoesNotContain("configuring project")
+    }
+
+    def "cannot disable configuration cache when option is enabled"() {
+        buildFile """
+            println "configuring project"
+            task thing { }
+        """
+
+        when:
+        configurationCacheFails("thing", "--no-configuration-cache")
+
+        then:
+        failure.assertHasDescription("The configuration cache cannot be disabled when isolated projects is enabled.")
+    }
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheServices.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheServices.kt
index 2796697..302442c 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheServices.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheServices.kt
@@ -37,6 +37,12 @@
         }
     }
 
+    override fun registerBuildSessionServices(registration: ServiceRegistration) {
+        registration.run {
+            add(DefaultBuildTreeModelControllerServices::class.java)
+        }
+    }
+
     override fun registerBuildTreeServices(registration: ServiceRegistration) {
         registration.run {
             add(BuildTreeListenerManager::class.java)
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultBuildModelControllerFactory.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultBuildModelControllerFactory.kt
deleted file mode 100644
index 9ebb8d0..0000000
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultBuildModelControllerFactory.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2021 the original author or authors.
- *
- * Licensed 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.gradle.configurationcache
-
-import org.gradle.api.internal.GradleInternal
-import org.gradle.configuration.ProjectsPreparer
-import org.gradle.configurationcache.extensions.get
-import org.gradle.configurationcache.initialization.ConfigurationCacheStartParameter
-import org.gradle.internal.build.BuildModelControllerFactory
-import org.gradle.initialization.ConfigurationCache
-import org.gradle.initialization.ConfigurationCacheAwareBuildModelController
-import org.gradle.initialization.SettingsPreparer
-import org.gradle.initialization.TaskExecutionPreparer
-import org.gradle.initialization.VintageBuildModelController
-import org.gradle.internal.build.BuildModelController
-
-
-class DefaultBuildModelControllerFactory(
-    val startParameter: ConfigurationCacheStartParameter,
-) : BuildModelControllerFactory {
-    override fun create(gradle: GradleInternal): BuildModelController {
-        val projectsPreparer: ProjectsPreparer = gradle.services.get()
-        val settingsPreparer: SettingsPreparer = gradle.services.get()
-        val taskExecutionPreparer: TaskExecutionPreparer = gradle.services.get()
-        val configurationCache: ConfigurationCache = gradle.services.get()
-        val vintageController = VintageBuildModelController(gradle, projectsPreparer, settingsPreparer, taskExecutionPreparer)
-        return if (startParameter.isEnabled) {
-            ConfigurationCacheAwareBuildModelController(gradle, vintageController, configurationCache)
-        } else {
-            vintageController
-        }
-    }
-}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultBuildModelControllerServices.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultBuildModelControllerServices.kt
index a933529..eec2618 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultBuildModelControllerServices.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultBuildModelControllerServices.kt
@@ -16,6 +16,16 @@
 
 package org.gradle.configurationcache
 
+import org.gradle.api.internal.GradleInternal
+import org.gradle.configuration.ProjectsPreparer
+import org.gradle.configurationcache.extensions.get
+import org.gradle.configurationcache.initialization.ConfigurationCacheStartParameter
+import org.gradle.initialization.ConfigurationCache
+import org.gradle.initialization.ConfigurationCacheAwareBuildModelController
+import org.gradle.initialization.SettingsPreparer
+import org.gradle.initialization.TaskExecutionPreparer
+import org.gradle.initialization.VintageBuildModelController
+import org.gradle.internal.build.BuildModelController
 import org.gradle.internal.build.BuildModelControllerServices
 import org.gradle.internal.service.scopes.BuildScopeServices
 
@@ -23,7 +33,22 @@
 class DefaultBuildModelControllerServices : BuildModelControllerServices {
     override fun supplyBuildScopeServices(services: BuildScopeServices) {
         services.register {
-            add(DefaultBuildModelControllerFactory::class.java)
+            addProvider(BuildScopeServicesProvider())
+        }
+    }
+
+    class BuildScopeServicesProvider {
+        fun createBuildModelController(gradle: GradleInternal, startParameter: ConfigurationCacheStartParameter): BuildModelController {
+            val projectsPreparer: ProjectsPreparer = gradle.services.get()
+            val settingsPreparer: SettingsPreparer = gradle.services.get()
+            val taskExecutionPreparer: TaskExecutionPreparer = gradle.services.get()
+            val configurationCache: ConfigurationCache = gradle.services.get()
+            val vintageController = VintageBuildModelController(gradle, projectsPreparer, settingsPreparer, taskExecutionPreparer)
+            return if (startParameter.isEnabled) {
+                ConfigurationCacheAwareBuildModelController(gradle, vintageController, configurationCache)
+            } else {
+                vintageController
+            }
         }
     }
 }
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultBuildTreeModelControllerServices.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultBuildTreeModelControllerServices.kt
new file mode 100644
index 0000000..435d092
--- /dev/null
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultBuildTreeModelControllerServices.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed 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.gradle.configurationcache
+
+import org.gradle.api.GradleException
+import org.gradle.api.internal.BuildType
+import org.gradle.api.internal.StartParameterInternal
+import org.gradle.internal.buildtree.BuildModelParameters
+import org.gradle.internal.buildtree.BuildTreeModelControllerServices
+import org.gradle.util.internal.IncubationLogger
+
+
+class DefaultBuildTreeModelControllerServices : BuildTreeModelControllerServices {
+    override fun servicesForBuildTree(runsTasks: Boolean, createsModel: Boolean, startParameter: StartParameterInternal): BuildTreeModelControllerServices.Supplier {
+        // Isolated projects also implies configuration cache
+        if (startParameter.isolatedProjects.get() && !startParameter.configurationCache.get()) {
+            if (startParameter.configurationCache.isExplicit) {
+                throw GradleException("The configuration cache cannot be disabled when isolated projects is enabled.")
+            }
+        }
+
+        val modelParameters = if (createsModel) {
+            // When creating a model, disable certain features
+            BuildModelParameters(false, false, startParameter.isolatedProjects.get())
+        } else {
+            val isolatedProjects = startParameter.isolatedProjects.get()
+            val configurationCache = startParameter.configurationCache.get() || isolatedProjects
+            BuildModelParameters(startParameter.isConfigureOnDemand, configurationCache, isolatedProjects)
+        }
+
+        if (!startParameter.isConfigurationCacheQuiet) {
+            if (modelParameters.isIsolatedProjects) {
+                IncubationLogger.incubatingFeatureUsed("Isolated projects")
+            } else if (modelParameters.isConfigurationCache) {
+                IncubationLogger.incubatingFeatureUsed("Configuration cache")
+            }
+        }
+
+        return BuildTreeModelControllerServices.Supplier { registration ->
+            val buildType = if (runsTasks) BuildType.TASKS else BuildType.MODEL
+            registration.add(BuildType::class.java, buildType)
+            registration.add(BuildModelParameters::class.java, modelParameters)
+        }
+    }
+
+    override fun servicesForNestedBuildTree(startParameter: StartParameterInternal): BuildTreeModelControllerServices.Supplier {
+        return BuildTreeModelControllerServices.Supplier { registration ->
+            registration.add(BuildType::class.java, BuildType.TASKS)
+            // Configuration cache is not supported for nested build trees
+            registration.add(BuildModelParameters::class.java, BuildModelParameters(startParameter.isConfigureOnDemand, false, false))
+        }
+    }
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultConfigurationCache.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultConfigurationCache.kt
index 78c0462f..5888b8b 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultConfigurationCache.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultConfigurationCache.kt
@@ -38,7 +38,6 @@
 import org.gradle.internal.operations.BuildOperationExecutor
 import org.gradle.internal.vfs.FileSystemAccess
 import org.gradle.internal.watch.vfs.BuildLifecycleAwareVirtualFileSystem
-import org.gradle.util.internal.IncubationLogger
 import java.io.File
 import java.io.FileInputStream
 import java.io.OutputStream
@@ -310,9 +309,6 @@
 
     private
     fun logBootstrapSummary(message: String, vararg args: Any?) {
-        if (!startParameter.isQuiet) {
-            IncubationLogger.incubatingFeatureUsed("Configuration cache")
-        }
         log(message, *args)
     }
 
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/build/ConfigurationCacheIncludedBuildState.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/build/ConfigurationCacheIncludedBuildState.kt
index 0cc175d..db109ac 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/build/ConfigurationCacheIncludedBuildState.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/build/ConfigurationCacheIncludedBuildState.kt
@@ -20,11 +20,10 @@
 import org.gradle.api.internal.BuildDefinition
 import org.gradle.api.internal.GradleInternal
 import org.gradle.composite.internal.DefaultIncludedBuild
-import org.gradle.internal.build.BuildModelControllerFactory
-import org.gradle.internal.build.BuildModelControllerServices
-import org.gradle.internal.build.BuildLifecycleControllerFactory
 import org.gradle.internal.build.BuildLifecycleController
+import org.gradle.internal.build.BuildLifecycleControllerFactory
 import org.gradle.internal.build.BuildModelController
+import org.gradle.internal.build.BuildModelControllerServices
 import org.gradle.internal.build.BuildState
 import org.gradle.internal.buildtree.BuildTreeController
 import org.gradle.internal.service.scopes.BuildScopeServices
@@ -45,8 +44,8 @@
 ) : DefaultIncludedBuild(buildIdentifier, identityPath, buildDefinition, isImplicit, owner, buildTree, parentLease, buildLifecycleControllerFactory, buildModelControllerServices) {
     override fun createGradleLauncher(owner: BuildState, buildTree: BuildTreeController, buildLifecycleControllerFactory: BuildLifecycleControllerFactory, buildModelControllerServices: BuildModelControllerServices): BuildLifecycleController {
         val buildScopeServices = object : BuildScopeServices(buildTree.services) {
-            fun createBuildModelControllerFactory(): BuildModelControllerFactory {
-                return NoOpBuildModelControllerFactory
+            fun createBuildModelController(gradle: GradleInternal): BuildModelController {
+                return NoOpBuildModelController(gradle)
             }
         }
         return buildLifecycleControllerFactory.newInstance(buildDefinition, this, owner.mutableModel, buildScopeServices)
@@ -54,14 +53,6 @@
 }
 
 
-private
-object NoOpBuildModelControllerFactory : BuildModelControllerFactory {
-    override fun create(gradle: GradleInternal): BuildModelController {
-        return NoOpBuildModelController(gradle)
-    }
-}
-
-
 // The model for this build is already fully populated and the tasks scheduled when it is created, so this controller does not need to do anything
 private
 class NoOpBuildModelController(val gradle: GradleInternal) : BuildModelController {
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/ConfigurationCacheStartParameter.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/ConfigurationCacheStartParameter.kt
index 9c2255e..5f292ba 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/ConfigurationCacheStartParameter.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/ConfigurationCacheStartParameter.kt
@@ -21,6 +21,7 @@
 import org.gradle.configurationcache.extensions.unsafeLazy
 import org.gradle.initialization.StartParameterBuildOptions.ConfigurationCacheProblemsOption
 import org.gradle.initialization.layout.BuildLayout
+import org.gradle.internal.buildtree.BuildModelParameters
 import org.gradle.internal.service.scopes.Scopes
 import org.gradle.internal.service.scopes.ServiceScope
 import java.io.File
@@ -29,6 +30,7 @@
 @ServiceScope(Scopes.BuildTree::class)
 class ConfigurationCacheStartParameter(
     private val buildLayout: BuildLayout,
+    private val buildModelParameters: BuildModelParameters,
     startParameter: StartParameter
 ) {
 
@@ -36,7 +38,7 @@
     val startParameter = startParameter as StartParameterInternal
 
     val isEnabled: Boolean
-        get() = startParameter.isConfigurationCache
+        get() = buildModelParameters.isConfigurationCache
 
     val isQuiet: Boolean
         get() = startParameter.isConfigurationCacheQuiet
diff --git a/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/ConfigurationCacheKeyTest.kt b/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/ConfigurationCacheKeyTest.kt
index 5807f75..e03dc33 100644
--- a/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/ConfigurationCacheKeyTest.kt
+++ b/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/ConfigurationCacheKeyTest.kt
@@ -19,6 +19,7 @@
 import org.gradle.api.internal.StartParameterInternal
 import org.gradle.configurationcache.initialization.ConfigurationCacheStartParameter
 import org.gradle.initialization.layout.BuildLayout
+import org.gradle.internal.buildtree.BuildModelParameters
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.hamcrest.CoreMatchers.equalTo
 import org.hamcrest.CoreMatchers.not
@@ -97,6 +98,7 @@
                     file("settings"),
                     null
                 ),
+                BuildModelParameters(false, true, false),
                 StartParameterInternal().apply(configure)
             )
         ).string
diff --git a/subprojects/core-api/src/main/java/org/gradle/internal/DefaultTaskExecutionRequest.java b/subprojects/core-api/src/main/java/org/gradle/internal/DefaultTaskExecutionRequest.java
index 04ac1e3..bc5e8d3 100644
--- a/subprojects/core-api/src/main/java/org/gradle/internal/DefaultTaskExecutionRequest.java
+++ b/subprojects/core-api/src/main/java/org/gradle/internal/DefaultTaskExecutionRequest.java
@@ -91,6 +91,7 @@
         return "DefaultTaskExecutionRequest{"
                 + "args=" + args
                 + ",projectPath='" + projectPath + '\''
+                + ",rootDir='" + rootDir + '\''
                 + '}';
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/BuildDefinition.java b/subprojects/core/src/main/java/org/gradle/api/internal/BuildDefinition.java
index 8b6448b..155ead7 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/BuildDefinition.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/BuildDefinition.java
@@ -125,7 +125,6 @@
         StartParameterInternal includedBuildStartParam = startParameter.newBuild();
         includedBuildStartParam.setCurrentDir(buildRootDir);
         includedBuildStartParam.doNotSearchUpwards();
-        includedBuildStartParam.setConfigureOnDemand(false);
         includedBuildStartParam.setInitScripts(startParameter.getInitScripts());
         includedBuildStartParam.setExcludedTaskNames(startParameter.getExcludedTaskNames());
         return includedBuildStartParam;
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/GradleInternal.java b/subprojects/core/src/main/java/org/gradle/api/internal/GradleInternal.java
index dcb0df2..693d074 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/GradleInternal.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/GradleInternal.java
@@ -27,7 +27,9 @@
 import org.gradle.internal.build.PublicBuildPath;
 import org.gradle.internal.scan.UsedByScanPlugin;
 import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.internal.service.scopes.Scopes;
 import org.gradle.internal.service.scopes.ServiceRegistryFactory;
+import org.gradle.internal.service.scopes.ServiceScope;
 import org.gradle.util.Path;
 
 import javax.annotation.Nullable;
@@ -38,6 +40,7 @@
  * consumption.
  */
 @UsedByScanPlugin
+@ServiceScope(Scopes.Build.class)
 public interface GradleInternal extends Gradle, PluginAwareInternal {
     /**
      * {@inheritDoc}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/StartParameterInternal.java b/subprojects/core/src/main/java/org/gradle/api/internal/StartParameterInternal.java
index 8f46c0e..3fc1a66 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/StartParameterInternal.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/StartParameterInternal.java
@@ -19,6 +19,8 @@
 import org.gradle.StartParameter;
 import org.gradle.initialization.BuildLayoutParameters;
 import org.gradle.initialization.StartParameterBuildOptions.ConfigurationCacheProblemsOption;
+import org.gradle.internal.buildoption.BuildOption;
+import org.gradle.internal.buildtree.BuildModelParameters;
 import org.gradle.internal.watch.vfs.WatchMode;
 
 import javax.annotation.Nullable;
@@ -37,7 +39,8 @@
     private boolean watchFileSystemDebugLogging;
     private boolean vfsVerboseLogging;
 
-    private boolean configurationCache;
+    private BuildOption.Value<Boolean> configurationCache = BuildOption.Value.defaultValue(false);
+    private BuildOption.Value<Boolean> isolatedProjects = BuildOption.Value.defaultValue(false);
     private ConfigurationCacheProblemsOption.Value configurationCacheProblems = ConfigurationCacheProblemsOption.Value.FAIL;
     private int configurationCacheMaxProblems = 512;
     private boolean configurationCacheRecreateCache;
@@ -69,6 +72,7 @@
         p.watchFileSystemDebugLogging = watchFileSystemDebugLogging;
         p.vfsVerboseLogging = vfsVerboseLogging;
         p.configurationCache = configurationCache;
+        p.isolatedProjects = isolatedProjects;
         p.configurationCacheProblems = configurationCacheProblems;
         p.configurationCacheMaxProblems = configurationCacheMaxProblems;
         p.configurationCacheRecreateCache = configurationCacheRecreateCache;
@@ -126,14 +130,35 @@
         this.vfsVerboseLogging = vfsVerboseLogging;
     }
 
+    /**
+     * Used by the Kotlin plugin, via reflection.
+     */
+    @Deprecated
     public boolean isConfigurationCache() {
+        return getConfigurationCache().get();
+    }
+
+    /**
+     * Is the configuration cache requested? Note: depending on the build action, this may not be the final value for this option.
+     *
+     * Consider querying {@link BuildModelParameters} instead.
+     */
+    public BuildOption.Value<Boolean> getConfigurationCache() {
         return configurationCache;
     }
 
-    public void setConfigurationCache(boolean configurationCache) {
+    public void setConfigurationCache(BuildOption.Value<Boolean> configurationCache) {
         this.configurationCache = configurationCache;
     }
 
+    public BuildOption.Value<Boolean> getIsolatedProjects() {
+        return isolatedProjects;
+    }
+
+    public void setIsolatedProjects(BuildOption.Value<Boolean> isolatedProjects) {
+        this.isolatedProjects = isolatedProjects;
+    }
+
     public ConfigurationCacheProblemsOption.Value getConfigurationCacheProblems() {
         return configurationCacheProblems;
     }
diff --git a/subprojects/core/src/main/java/org/gradle/configuration/DefaultProjectsPreparer.java b/subprojects/core/src/main/java/org/gradle/configuration/DefaultProjectsPreparer.java
index 3486952..92c9aed 100644
--- a/subprojects/core/src/main/java/org/gradle/configuration/DefaultProjectsPreparer.java
+++ b/subprojects/core/src/main/java/org/gradle/configuration/DefaultProjectsPreparer.java
@@ -19,27 +19,31 @@
 import org.gradle.execution.ProjectConfigurer;
 import org.gradle.initialization.ModelConfigurationListener;
 import org.gradle.initialization.ProjectsEvaluatedNotifier;
+import org.gradle.internal.buildtree.BuildModelParameters;
 import org.gradle.internal.operations.BuildOperationExecutor;
 import org.gradle.util.internal.IncubationLogger;
 
 public class DefaultProjectsPreparer implements ProjectsPreparer {
     private final BuildOperationExecutor buildOperationExecutor;
     private final ProjectConfigurer projectConfigurer;
+    private final BuildModelParameters buildModelParameters;
     private final ModelConfigurationListener modelConfigurationListener;
 
     public DefaultProjectsPreparer(
-            ProjectConfigurer projectConfigurer,
-            ModelConfigurationListener modelConfigurationListener,
-            BuildOperationExecutor buildOperationExecutor
+        ProjectConfigurer projectConfigurer,
+        BuildModelParameters buildModelParameters,
+        ModelConfigurationListener modelConfigurationListener,
+        BuildOperationExecutor buildOperationExecutor
     ) {
         this.projectConfigurer = projectConfigurer;
+        this.buildModelParameters = buildModelParameters;
         this.modelConfigurationListener = modelConfigurationListener;
         this.buildOperationExecutor = buildOperationExecutor;
     }
 
     @Override
     public void prepareProjects(GradleInternal gradle) {
-        if (gradle.getStartParameter().isConfigureOnDemand()) {
+        if (buildModelParameters.isConfigureOnDemand() && gradle.isRootBuild()) {
             IncubationLogger.incubatingFeatureUsed("Configuration on demand");
             projectConfigurer.configure(gradle.getRootProject());
         } else {
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/BuildOptionBuildOperationProgressEventsEmitter.java b/subprojects/core/src/main/java/org/gradle/initialization/BuildOptionBuildOperationProgressEventsEmitter.java
index 044b1c5..bc9cde4 100644
--- a/subprojects/core/src/main/java/org/gradle/initialization/BuildOptionBuildOperationProgressEventsEmitter.java
+++ b/subprojects/core/src/main/java/org/gradle/initialization/BuildOptionBuildOperationProgressEventsEmitter.java
@@ -16,27 +16,32 @@
 
 package org.gradle.initialization;
 
-import org.gradle.api.internal.StartParameterInternal;
+import org.gradle.internal.buildtree.BuildModelParameters;
 import org.gradle.internal.configurationcache.options.ConfigurationCacheSettingsFinalizedProgressDetails;
 import org.gradle.internal.operations.BuildOperationProgressEventEmitter;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
 
 import javax.inject.Inject;
 
+@ServiceScope(Scopes.BuildTree.class)
 public class BuildOptionBuildOperationProgressEventsEmitter {
 
     private final BuildOperationProgressEventEmitter eventEmitter;
+    private final BuildModelParameters buildModelParameters;
 
     @Inject
-    public BuildOptionBuildOperationProgressEventsEmitter(BuildOperationProgressEventEmitter eventEmitter) {
+    public BuildOptionBuildOperationProgressEventsEmitter(BuildOperationProgressEventEmitter eventEmitter, BuildModelParameters buildModelParameters) {
         this.eventEmitter = eventEmitter;
+        this.buildModelParameters = buildModelParameters;
     }
 
     @SuppressWarnings({"Anonymous2MethodRef", "Convert2Lambda"})
-    public void emit(StartParameterInternal startParameterInternal) {
+    public void emit() {
         eventEmitter.emitNowForCurrent(new ConfigurationCacheSettingsFinalizedProgressDetails() {
             @Override
             public boolean isEnabled() {
-                return startParameterInternal.isConfigurationCache();
+                return buildModelParameters.isConfigurationCache();
             }
         });
     }
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/DefaultTaskExecutionPreparer.java b/subprojects/core/src/main/java/org/gradle/initialization/DefaultTaskExecutionPreparer.java
index e329ec9..bd384ce 100644
--- a/subprojects/core/src/main/java/org/gradle/initialization/DefaultTaskExecutionPreparer.java
+++ b/subprojects/core/src/main/java/org/gradle/initialization/DefaultTaskExecutionPreparer.java
@@ -20,17 +20,23 @@
 import org.gradle.composite.internal.IncludedBuildControllers;
 import org.gradle.execution.BuildConfigurationActionExecuter;
 import org.gradle.execution.taskgraph.TaskExecutionGraphInternal;
+import org.gradle.internal.buildtree.BuildModelParameters;
 import org.gradle.internal.operations.BuildOperationExecutor;
 
 public class DefaultTaskExecutionPreparer implements TaskExecutionPreparer {
     private final BuildOperationExecutor buildOperationExecutor;
     private final BuildConfigurationActionExecuter buildConfigurationActionExecuter;
     private final IncludedBuildControllers includedBuildControllers;
+    private final BuildModelParameters buildModelParameters;
 
-    public DefaultTaskExecutionPreparer(BuildConfigurationActionExecuter buildConfigurationActionExecuter, IncludedBuildControllers includedBuildControllers, BuildOperationExecutor buildOperationExecutor) {
+    public DefaultTaskExecutionPreparer(BuildConfigurationActionExecuter buildConfigurationActionExecuter,
+                                        IncludedBuildControllers includedBuildControllers,
+                                        BuildOperationExecutor buildOperationExecutor,
+                                        BuildModelParameters buildModelParameters) {
         this.buildConfigurationActionExecuter = buildConfigurationActionExecuter;
         this.includedBuildControllers = includedBuildControllers;
         this.buildOperationExecutor = buildOperationExecutor;
+        this.buildModelParameters = buildModelParameters;
     }
 
     @Override
@@ -42,7 +48,7 @@
 
         includedBuildControllers.populateTaskGraphs();
 
-        if (gradle.getStartParameter().isConfigureOnDemand()) {
+        if (buildModelParameters.isConfigureOnDemand() && gradle.isRootBuild()) {
             new ProjectsEvaluatedNotifier(buildOperationExecutor).notify(gradle);
         }
     }
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/StartParameterBuildOptions.java b/subprojects/core/src/main/java/org/gradle/initialization/StartParameterBuildOptions.java
index a6fcd33..9f4c1c9 100644
--- a/subprojects/core/src/main/java/org/gradle/initialization/StartParameterBuildOptions.java
+++ b/subprojects/core/src/main/java/org/gradle/initialization/StartParameterBuildOptions.java
@@ -75,6 +75,7 @@
         options.add(new ExportKeysOption());
         options.add(new ConfigurationCacheProblemsOption());
         options.add(new ConfigurationCacheOption());
+        options.add(new IsolatedProjectsOption());
         options.add(new ConfigurationCacheMaxProblemsOption());
         options.add(new ConfigurationCacheRecreateOption());
         options.add(new ConfigurationCacheQuietOption());
@@ -478,7 +479,18 @@
 
         @Override
         public void applyTo(boolean value, StartParameterInternal settings, Origin origin) {
-            settings.setConfigurationCache(value);
+            settings.setConfigurationCache(BuildOption.Value.value(value));
+        }
+    }
+
+    public static class IsolatedProjectsOption extends BooleanBuildOption<StartParameterInternal> {
+        public IsolatedProjectsOption() {
+            super("org.gradle.unsafe.isolated-projects");
+        }
+
+        @Override
+        public void applyTo(boolean value, StartParameterInternal settings, Origin origin) {
+            settings.setIsolatedProjects(Value.value(value));
         }
     }
 
diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/BuildModelControllerFactory.java b/subprojects/core/src/main/java/org/gradle/internal/build/BuildModelControllerFactory.java
deleted file mode 100644
index defa837..0000000
--- a/subprojects/core/src/main/java/org/gradle/internal/build/BuildModelControllerFactory.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2021 the original author or authors.
- *
- * Licensed 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.gradle.internal.build;
-
-import org.gradle.api.internal.GradleInternal;
-import org.gradle.internal.service.scopes.Scopes;
-import org.gradle.internal.service.scopes.ServiceScope;
-
-@ServiceScope(Scopes.Build.class)
-public interface BuildModelControllerFactory {
-    /**
-     * Creates the {@link BuildModelController} for the given build model instance.
-     */
-    BuildModelController create(GradleInternal gradle);
-}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/BuildModelControllerServices.java b/subprojects/core/src/main/java/org/gradle/internal/build/BuildModelControllerServices.java
index c74b413..cd51ac3 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/build/BuildModelControllerServices.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/build/BuildModelControllerServices.java
@@ -23,7 +23,7 @@
 @ServiceScope(Scopes.BuildTree.class)
 public interface BuildModelControllerServices {
     /**
-     * Registers the services required to produce a {@link BuildModelControllerFactory} for the given build.
+     * Registers the services required to produce a {@link BuildModelController} for the given build.
      */
     void supplyBuildScopeServices(BuildScopeServices services);
 }
diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/DefaultBuildLifecycleController.java b/subprojects/core/src/main/java/org/gradle/internal/build/DefaultBuildLifecycleController.java
index 39154dc..dea8c12 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/build/DefaultBuildLifecycleController.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/build/DefaultBuildLifecycleController.java
@@ -22,7 +22,6 @@
 import org.gradle.execution.BuildWorkExecutor;
 import org.gradle.execution.MultipleBuildFailures;
 import org.gradle.initialization.BuildCompletionListener;
-import org.gradle.initialization.BuildOptionBuildOperationProgressEventsEmitter;
 import org.gradle.initialization.exception.ExceptionAnalyser;
 import org.gradle.initialization.internal.InternalBuildFinishedListener;
 import org.gradle.internal.concurrent.CompositeStoppable;
@@ -55,7 +54,6 @@
     private final BuildScopeServices buildServices;
     private final GradleInternal gradle;
     private final BuildModelController modelController;
-    private final BuildOptionBuildOperationProgressEventsEmitter buildOptionBuildOperationProgressEventsEmitter;
 
     private Stage stage = Stage.Created;
     @Nullable
@@ -69,8 +67,7 @@
         BuildCompletionListener buildCompletionListener,
         InternalBuildFinishedListener buildFinishedListener,
         BuildWorkExecutor buildExecuter,
-        BuildScopeServices buildServices,
-        BuildOptionBuildOperationProgressEventsEmitter buildOptionBuildOperationProgressEventsEmitter
+        BuildScopeServices buildServices
     ) {
         this.gradle = gradle;
         this.modelController = buildModelController;
@@ -80,7 +77,6 @@
         this.buildCompletionListener = buildCompletionListener;
         this.buildFinishedListener = buildFinishedListener;
         this.buildServices = buildServices;
-        this.buildOptionBuildOperationProgressEventsEmitter = buildOptionBuildOperationProgressEventsEmitter;
     }
 
     @Override
@@ -129,9 +125,6 @@
             throw new IllegalStateException("Cannot do further work as this build has failed.", stageFailure);
         }
         try {
-            if (stage == Stage.Created && gradle.isRootBuild()) {
-                buildOptionBuildOperationProgressEventsEmitter.emit(gradle.getStartParameter());
-            }
             try {
                 return action.apply(modelController);
             } finally {
diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/DefaultBuildLifecycleControllerFactory.java b/subprojects/core/src/main/java/org/gradle/internal/build/DefaultBuildLifecycleControllerFactory.java
index eff1c74..94de6f7 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/build/DefaultBuildLifecycleControllerFactory.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/build/DefaultBuildLifecycleControllerFactory.java
@@ -23,7 +23,6 @@
 import org.gradle.api.logging.configuration.ShowStacktrace;
 import org.gradle.execution.BuildWorkExecutor;
 import org.gradle.initialization.BuildCompletionListener;
-import org.gradle.initialization.BuildOptionBuildOperationProgressEventsEmitter;
 import org.gradle.initialization.exception.ExceptionAnalyser;
 import org.gradle.initialization.internal.InternalBuildFinishedListener;
 import org.gradle.internal.deprecation.DeprecationLogger;
@@ -43,27 +42,20 @@
 public class DefaultBuildLifecycleControllerFactory implements BuildLifecycleControllerFactory {
     @Override
     public BuildLifecycleController newInstance(BuildDefinition buildDefinition, BuildState owner, @Nullable GradleInternal parentModel, BuildScopeServices buildScopeServices) {
-        return doNewInstance(buildDefinition, owner, parentModel, buildScopeServices);
-    }
+        StartParameter startParameter = buildDefinition.getStartParameter();
 
-    private BuildLifecycleController doNewInstance(
-        BuildDefinition buildDefinition,
-        BuildState owner,
-        @Nullable GradleInternal parent,
-        BuildScopeServices serviceRegistry
-    ) {
-        serviceRegistry.add(BuildDefinition.class, buildDefinition);
-        serviceRegistry.add(BuildState.class, owner);
+        buildScopeServices.add(BuildDefinition.class, buildDefinition);
+        buildScopeServices.add(BuildState.class, owner);
+        buildScopeServices.addProvider(new GradleModelProvider(parentModel, startParameter));
 
-        final ListenerManager listenerManager = serviceRegistry.get(ListenerManager.class);
-        for (Action<ListenerManager> action : serviceRegistry.getAll(BuildScopeListenerManagerAction.class)) {
+        final ListenerManager listenerManager = buildScopeServices.get(ListenerManager.class);
+        for (Action<ListenerManager> action : buildScopeServices.getAll(BuildScopeListenerManagerAction.class)) {
             action.execute(listenerManager);
         }
 
         ScriptUsageLocationReporter usageLocationReporter = new ScriptUsageLocationReporter();
         listenerManager.addListener(usageLocationReporter);
 
-        StartParameter startParameter = buildDefinition.getStartParameter();
         ShowStacktrace showStacktrace = startParameter.getShowStacktrace();
         switch (showStacktrace) {
             case ALWAYS:
@@ -74,7 +66,7 @@
                 LoggingDeprecatedFeatureHandler.setTraceLoggingEnabled(false);
         }
 
-        DeprecationLogger.init(usageLocationReporter, startParameter.getWarningMode(), serviceRegistry.get(BuildOperationProgressEventEmitter.class));
+        DeprecationLogger.init(usageLocationReporter, startParameter.getWarningMode(), buildScopeServices.get(BuildOperationProgressEventEmitter.class));
         @SuppressWarnings("deprecation")
         File customSettingsFile = startParameter.getSettingsFile();
         if (customSettingsFile != null) {
@@ -92,38 +84,39 @@
                 .nagUser();
         }
 
-        GradleInternal gradle = serviceRegistry.get(Instantiator.class).newInstance(
-            DefaultGradle.class,
-            parent,
-            startParameter,
-            serviceRegistry.get(ServiceRegistryFactory.class)
-        );
+        GradleInternal gradle = buildScopeServices.get(GradleInternal.class);
 
-        BuildModelControllerFactory buildModelControllerFactory = serviceRegistry.get(BuildModelControllerFactory.class);
-        BuildModelController buildModelController = buildModelControllerFactory.create(gradle);
-
-        return createDefaultGradleLauncher(gradle, buildModelController, serviceRegistry);
-    }
-
-    private BuildLifecycleController createDefaultGradleLauncher(
-        GradleInternal gradle,
-        BuildModelController buildModelController,
-        BuildScopeServices serviceRegistry
-    ) {
-        final ListenerManager listenerManager = serviceRegistry.get(ListenerManager.class);
+        BuildModelController buildModelController = buildScopeServices.get(BuildModelController.class);
 
         return new DefaultBuildLifecycleController(
             gradle,
             buildModelController,
-            serviceRegistry.get(ExceptionAnalyser.class),
+            buildScopeServices.get(ExceptionAnalyser.class),
             gradle.getBuildListenerBroadcaster(),
             listenerManager.getBroadcaster(BuildCompletionListener.class),
             listenerManager.getBroadcaster(InternalBuildFinishedListener.class),
             gradle.getServices().get(BuildWorkExecutor.class),
-            serviceRegistry,
-            new BuildOptionBuildOperationProgressEventsEmitter(
-                gradle.getServices().get(BuildOperationProgressEventEmitter.class)
-            )
+            buildScopeServices
         );
     }
+
+    private static class GradleModelProvider {
+        @Nullable
+        private final GradleInternal parentModel;
+        private final StartParameter startParameter;
+
+        private GradleModelProvider(@Nullable GradleInternal parentModel, StartParameter startParameter) {
+            this.parentModel = parentModel;
+            this.startParameter = startParameter;
+        }
+
+        GradleInternal createGradleModel(Instantiator instantiator, ServiceRegistryFactory serviceRegistryFactory) {
+            return instantiator.newInstance(
+                DefaultGradle.class,
+                parentModel,
+                startParameter,
+                serviceRegistryFactory
+            );
+        }
+    }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildModelParameters.java b/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildModelParameters.java
new file mode 100644
index 0000000..309d832
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildModelParameters.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed 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.gradle.internal.buildtree;
+
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+@ServiceScope(Scopes.BuildTree.class)
+public class BuildModelParameters {
+    private final boolean configureOnDemand;
+    private final boolean configurationCache;
+    private final boolean isolatedProjects;
+
+    public BuildModelParameters(boolean configureOnDemand, boolean configurationCache, boolean isolatedProjects) {
+        this.configureOnDemand = configureOnDemand;
+        this.configurationCache = configurationCache;
+        this.isolatedProjects = isolatedProjects;
+    }
+
+    public boolean isConfigureOnDemand() {
+        return configureOnDemand;
+    }
+
+    public boolean isConfigurationCache() {
+        return configurationCache;
+    }
+
+    public boolean isIsolatedProjects() {
+        return isolatedProjects;
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildTreeController.java b/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildTreeController.java
index 59d0b18..fe427e1 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildTreeController.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildTreeController.java
@@ -16,7 +16,6 @@
 
 package org.gradle.internal.buildtree;
 
-import org.gradle.api.internal.BuildType;
 import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.ServiceRegistryBuilder;
@@ -35,11 +34,11 @@
     private final ServiceRegistry services;
     private final DefaultBuildTreeContext context;
 
-    public BuildTreeController(ServiceRegistry parent, BuildType buildType) {
+    public BuildTreeController(ServiceRegistry parent, BuildTreeModelControllerServices.Supplier modelServices) {
         services = ServiceRegistryBuilder.builder()
             .displayName("build tree services")
             .parent(parent)
-            .provider(new BuildTreeScopeServices(this, buildType))
+            .provider(new BuildTreeScopeServices(this, modelServices))
             .build();
         context = new DefaultBuildTreeContext(services);
 
diff --git a/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildTreeModelControllerServices.java b/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildTreeModelControllerServices.java
new file mode 100644
index 0000000..2df9260
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildTreeModelControllerServices.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed 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.gradle.internal.buildtree;
+
+import org.gradle.api.internal.StartParameterInternal;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+@ServiceScope(Scopes.BuildSession.class)
+public interface BuildTreeModelControllerServices {
+    /**
+     * Creates a {@link Supplier} that will contribute services required for the model of a build tree with the given parameters.
+     *
+     * <p>Contributes the following services:</p>
+     * <ul>
+     *     <li>{@link org.gradle.api.internal.BuildType}</li>.
+     *     <li>{@link BuildModelParameters}</li>.
+     * <ul>
+     */
+    Supplier servicesForBuildTree(boolean runsTasks, boolean createsModel, StartParameterInternal startParameter);
+
+    /**
+     * Creates a {@link Supplier} that will contribute the services required for the model of a nested build tree with the given parameters.
+     *
+     * <p>Contributes the same services as {@link #servicesForBuildTree(boolean, boolean, StartParameterInternal)}.</p>
+     */
+    Supplier servicesForNestedBuildTree(StartParameterInternal startParameter);
+
+    interface Supplier {
+        void applyServicesTo(ServiceRegistration registration);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildTreeScopeServices.java b/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildTreeScopeServices.java
index 101b75d..4721119 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildTreeScopeServices.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildTreeScopeServices.java
@@ -16,12 +16,12 @@
 
 package org.gradle.internal.buildtree;
 
-import org.gradle.api.internal.BuildType;
 import org.gradle.api.internal.project.DefaultProjectStateRegistry;
 import org.gradle.api.internal.provider.ConfigurationTimeBarrier;
 import org.gradle.api.internal.provider.DefaultConfigurationTimeBarrier;
 import org.gradle.api.logging.configuration.LoggingConfiguration;
 import org.gradle.api.logging.configuration.ShowStacktrace;
+import org.gradle.initialization.BuildOptionBuildOperationProgressEventsEmitter;
 import org.gradle.initialization.exception.DefaultExceptionAnalyser;
 import org.gradle.initialization.exception.ExceptionAnalyser;
 import org.gradle.initialization.exception.MultipleBuildFailuresExceptionAnalyser;
@@ -41,11 +41,11 @@
  */
 public class BuildTreeScopeServices {
     private final BuildTreeController buildTree;
-    private final BuildType buildType;
+    private final BuildTreeModelControllerServices.Supplier modelServices;
 
-    public BuildTreeScopeServices(BuildTreeController buildTree, BuildType buildType) {
+    public BuildTreeScopeServices(BuildTreeController buildTree, BuildTreeModelControllerServices.Supplier modelServices) {
         this.buildTree = buildTree;
-        this.buildType = buildType;
+        this.modelServices = modelServices;
     }
 
     protected void configure(ServiceRegistration registration, List<PluginServiceRegistry> pluginServiceRegistries) {
@@ -53,9 +53,10 @@
             pluginServiceRegistry.registerBuildTreeServices(registration);
         }
         registration.add(BuildTreeController.class, buildTree);
-        registration.add(BuildType.class, buildType);
         registration.add(GradleEnterprisePluginManager.class);
         registration.add(DefaultBuildLifecycleControllerFactory.class);
+        registration.add(BuildOptionBuildOperationProgressEventsEmitter.class);
+        modelServices.applyServicesTo(registration);
     }
 
     protected ListenerManager createListenerManager(ListenerManager parent) {
diff --git a/subprojects/core/src/main/java/org/gradle/internal/buildtree/DefaultBuildTreeLifecycleController.java b/subprojects/core/src/main/java/org/gradle/internal/buildtree/DefaultBuildTreeLifecycleController.java
index 8195449..a74cce4 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/buildtree/DefaultBuildTreeLifecycleController.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/buildtree/DefaultBuildTreeLifecycleController.java
@@ -18,8 +18,8 @@
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.SettingsInternal;
 import org.gradle.composite.internal.IncludedBuildControllers;
-import org.gradle.internal.build.BuildLifecycleController;
 import org.gradle.initialization.exception.ExceptionAnalyser;
+import org.gradle.internal.build.BuildLifecycleController;
 import org.gradle.internal.work.WorkerLeaseService;
 
 import java.util.ArrayList;
@@ -37,7 +37,10 @@
     private final IncludedBuildControllers includedBuildControllers;
     private final ExceptionAnalyser exceptionAnalyser;
 
-    public DefaultBuildTreeLifecycleController(BuildLifecycleController buildLifecycleController, WorkerLeaseService workerLeaseService, IncludedBuildControllers includedBuildControllers, ExceptionAnalyser exceptionAnalyser) {
+    public DefaultBuildTreeLifecycleController(BuildLifecycleController buildLifecycleController,
+                                               WorkerLeaseService workerLeaseService,
+                                               IncludedBuildControllers includedBuildControllers,
+                                               ExceptionAnalyser exceptionAnalyser) {
         this.buildLifecycleController = buildLifecycleController;
         this.workerLeaseService = workerLeaseService;
         this.includedBuildControllers = includedBuildControllers;
diff --git a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/BuildScopeServiceRegistryFactory.java b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/BuildScopeServiceRegistryFactory.java
index 35defdb..d0b55aa 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/BuildScopeServiceRegistryFactory.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/BuildScopeServiceRegistryFactory.java
@@ -35,7 +35,7 @@
     @Override
     public ServiceRegistry createFor(Object domainObject) {
         if (domainObject instanceof GradleInternal) {
-            GradleScopeServices gradleServices = new GradleScopeServices(services, (GradleInternal) domainObject);
+            GradleScopeServices gradleServices = new GradleScopeServices(services);
             registries.add(gradleServices);
             return gradleServices;
         }
@@ -44,8 +44,7 @@
             registries.add(settingsServices);
             return settingsServices;
         }
-        throw new IllegalArgumentException(String.format("Cannot create services for unknown domain object of type %s.",
-                domainObject.getClass().getSimpleName()));
+        throw new IllegalArgumentException(String.format("Cannot create services for unknown domain object of type %s.", domainObject.getClass().getSimpleName()));
     }
 
     @Override
diff --git a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/BuildScopeServices.java b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/BuildScopeServices.java
index f05e1ab..8f531db 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/BuildScopeServices.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/BuildScopeServices.java
@@ -153,6 +153,7 @@
 import org.gradle.internal.build.DefaultPublicBuildPath;
 import org.gradle.internal.build.PublicBuildPath;
 import org.gradle.internal.buildevents.BuildStartedTime;
+import org.gradle.internal.buildtree.BuildModelParameters;
 import org.gradle.internal.classloader.ClassLoaderFactory;
 import org.gradle.internal.classpath.CachedClasspathTransformer;
 import org.gradle.internal.composite.DefaultBuildIncluder;
@@ -506,12 +507,13 @@
         return new TaskPathProjectEvaluator(cancellationToken);
     }
 
-    protected ProjectsPreparer createBuildConfigurer(ProjectConfigurer projectConfigurer, BuildSourceBuilder buildSourceBuilder, BuildStateRegistry buildStateRegistry, BuildLoader buildLoader, ListenerManager listenerManager, BuildOperationExecutor buildOperationExecutor) {
+    protected ProjectsPreparer createBuildConfigurer(ProjectConfigurer projectConfigurer, BuildSourceBuilder buildSourceBuilder, BuildStateRegistry buildStateRegistry, BuildLoader buildLoader, ListenerManager listenerManager, BuildOperationExecutor buildOperationExecutor, BuildModelParameters buildModelParameters) {
         ModelConfigurationListener modelConfigurationListener = listenerManager.getBroadcaster(ModelConfigurationListener.class);
         return new BuildOperationFiringProjectsPreparer(
             new BuildTreePreparingProjectsPreparer(
                 new DefaultProjectsPreparer(
                     projectConfigurer,
+                    buildModelParameters,
                     modelConfigurationListener,
                     buildOperationExecutor),
                 buildLoader,
diff --git a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/GradleScopeServices.java b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/GradleScopeServices.java
index 320663f..d795e7a 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/GradleScopeServices.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/GradleScopeServices.java
@@ -86,6 +86,7 @@
 import org.gradle.internal.Factory;
 import org.gradle.internal.build.BuildState;
 import org.gradle.internal.build.BuildStateRegistry;
+import org.gradle.internal.buildtree.BuildModelParameters;
 import org.gradle.internal.cleanup.DefaultBuildOutputCleanupRegistry;
 import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.internal.event.ListenerBroadcast;
@@ -121,9 +122,8 @@
 
     private final CompositeStoppable registries = new CompositeStoppable();
 
-    public GradleScopeServices(final ServiceRegistry parent, final GradleInternal gradle) {
+    public GradleScopeServices(final ServiceRegistry parent) {
         super(parent);
-        add(GradleInternal.class, gradle);
         register(registration -> {
             for (PluginServiceRegistry pluginServiceRegistry : parent.getAll(PluginServiceRegistry.class)) {
                 pluginServiceRegistry.registerGradleServices(registration);
@@ -160,9 +160,9 @@
         return new DefaultBuildConfigurationActionExecuter(Arrays.asList(new ExcludedTaskFilteringBuildConfigurationAction(taskSelector)), taskSelectionActions, projectStateRegistry);
     }
 
-    TaskExecutionPreparer createTaskExecutionPreparer(BuildConfigurationActionExecuter buildConfigurationActionExecuter, IncludedBuildControllers includedBuildControllers, BuildOperationExecutor buildOperationExecutor) {
+    TaskExecutionPreparer createTaskExecutionPreparer(BuildConfigurationActionExecuter buildConfigurationActionExecuter, IncludedBuildControllers includedBuildControllers, BuildOperationExecutor buildOperationExecutor, BuildModelParameters buildModelParameters) {
         return new BuildOperationFiringTaskExecutionPreparer(
-            new DefaultTaskExecutionPreparer(buildConfigurationActionExecuter, includedBuildControllers, buildOperationExecutor),
+            new DefaultTaskExecutionPreparer(buildConfigurationActionExecuter, includedBuildControllers, buildOperationExecutor, buildModelParameters),
             buildOperationExecutor);
     }
 
diff --git a/subprojects/core/src/main/java/org/gradle/testfixtures/internal/ProjectBuilderImpl.java b/subprojects/core/src/main/java/org/gradle/testfixtures/internal/ProjectBuilderImpl.java
index b4690a7..e68c507 100644
--- a/subprojects/core/src/main/java/org/gradle/testfixtures/internal/ProjectBuilderImpl.java
+++ b/subprojects/core/src/main/java/org/gradle/testfixtures/internal/ProjectBuilderImpl.java
@@ -19,7 +19,6 @@
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.component.BuildIdentifier;
 import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-import org.gradle.api.internal.BuildType;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.SettingsInternal;
 import org.gradle.api.internal.StartParameterInternal;
@@ -48,9 +47,9 @@
 import org.gradle.internal.build.RootBuildState;
 import org.gradle.internal.buildtree.BuildTreeController;
 import org.gradle.internal.buildtree.BuildTreeLifecycleController;
+import org.gradle.internal.buildtree.BuildTreeModelControllerServices;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.concurrent.Stoppable;
-import org.gradle.internal.instantiation.InstantiatorFactory;
 import org.gradle.internal.logging.services.LoggingServiceRegistry;
 import org.gradle.internal.nativeintegration.services.NativeServices;
 import org.gradle.internal.resources.DefaultResourceLockCoordinationService;
@@ -58,14 +57,12 @@
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.ServiceRegistryBuilder;
 import org.gradle.internal.service.scopes.GradleUserHomeScopeServiceRegistry;
-import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.internal.session.BuildSessionController;
 import org.gradle.internal.session.CrossBuildSessionState;
 import org.gradle.internal.time.Time;
 import org.gradle.internal.work.DefaultWorkerLeaseService;
 import org.gradle.internal.work.WorkerLeaseRegistry;
 import org.gradle.internal.work.WorkerLeaseService;
-import org.gradle.invocation.DefaultGradle;
 import org.gradle.util.Path;
 
 import javax.annotation.Nullable;
@@ -112,14 +109,15 @@
         CrossBuildSessionState crossBuildSessionState = new CrossBuildSessionState(globalServices, startParameter);
         GradleUserHomeScopeServiceRegistry userHomeServices = userHomeServicesOf(globalServices);
         BuildSessionController buildSessionController = new BuildSessionController(userHomeServices, crossBuildSessionState, startParameter, buildRequestMetaData, ClassPath.EMPTY, new DefaultBuildCancellationToken(), buildRequestMetaData.getClient(), new NoOpBuildEventConsumer());
-        BuildTreeController buildTreeController = new BuildTreeController(buildSessionController.getServices(), BuildType.TASKS);
-        TestBuildScopeServices buildServices = new TestBuildScopeServices(buildTreeController.getServices(), homeDir);
+        BuildTreeModelControllerServices.Supplier modelServices = buildSessionController.getServices().get(BuildTreeModelControllerServices.class).servicesForBuildTree(true, false, startParameter);
+        BuildTreeController buildTreeController = new BuildTreeController(buildSessionController.getServices(), modelServices);
+        TestBuildScopeServices buildServices = new TestBuildScopeServices(buildTreeController.getServices(), homeDir, startParameter);
         TestRootBuild build = new TestRootBuild(projectDir);
         buildServices.add(BuildState.class, build);
 
         buildServices.get(BuildStateRegistry.class).attachRootBuild(build);
 
-        GradleInternal gradle = buildServices.get(InstantiatorFactory.class).decorateLenient().newInstance(DefaultGradle.class, null, startParameter, buildServices.get(ServiceRegistryFactory.class));
+        GradleInternal gradle = buildServices.get(GradleInternal.class);
         gradle.setIncludedBuilds(Collections.emptyList());
         build.setGradle(gradle); // the TestRootBuild instance cannot be created after GradleInternal
 
diff --git a/subprojects/core/src/main/java/org/gradle/testfixtures/internal/TestBuildScopeServices.java b/subprojects/core/src/main/java/org/gradle/testfixtures/internal/TestBuildScopeServices.java
index e97ab10..b63f92d 100644
--- a/subprojects/core/src/main/java/org/gradle/testfixtures/internal/TestBuildScopeServices.java
+++ b/subprojects/core/src/main/java/org/gradle/testfixtures/internal/TestBuildScopeServices.java
@@ -16,6 +16,7 @@
 package org.gradle.testfixtures.internal;
 
 import org.gradle.api.internal.BuildDefinition;
+import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.StartParameterInternal;
 import org.gradle.configuration.GradleLauncherMetaData;
 import org.gradle.initialization.BuildCancellationToken;
@@ -23,17 +24,22 @@
 import org.gradle.initialization.DefaultBuildCancellationToken;
 import org.gradle.internal.installation.CurrentGradleInstallation;
 import org.gradle.internal.installation.GradleInstallation;
+import org.gradle.internal.instantiation.InstantiatorFactory;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.scopes.BuildScopeServices;
+import org.gradle.internal.service.scopes.ServiceRegistryFactory;
+import org.gradle.invocation.DefaultGradle;
 
 import java.io.File;
 
 public class TestBuildScopeServices extends BuildScopeServices {
     private final File homeDir;
+    private final StartParameterInternal startParameter;
 
-    public TestBuildScopeServices(ServiceRegistry parent, File homeDir) {
+    public TestBuildScopeServices(ServiceRegistry parent, File homeDir, StartParameterInternal startParameter) {
         super(parent);
         this.homeDir = homeDir;
+        this.startParameter = startParameter;
     }
 
     protected BuildDefinition createBuildDefinition(StartParameterInternal startParameter) {
@@ -51,4 +57,8 @@
     protected CurrentGradleInstallation createCurrentGradleInstallation() {
         return new CurrentGradleInstallation(new GradleInstallation(homeDir));
     }
+
+    protected GradleInternal createGradle(InstantiatorFactory instantiatorFactory, ServiceRegistryFactory serviceRegistryFactory) {
+        return instantiatorFactory.decorateLenient().newInstance(DefaultGradle.class, null, startParameter, serviceRegistryFactory);
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultProjectsPreparerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultProjectsPreparerTest.groovy
index 4c59303..c63c197 100644
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultProjectsPreparerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultProjectsPreparerTest.groovy
@@ -23,6 +23,7 @@
 import org.gradle.initialization.BuildLoader
 import org.gradle.initialization.ModelConfigurationListener
 import org.gradle.internal.build.BuildStateRegistry
+import org.gradle.internal.buildtree.BuildModelParameters
 import org.gradle.internal.operations.BuildOperationExecutor
 import spock.lang.Specification
 
@@ -33,9 +34,10 @@
     def projectConfigurer = Mock(ProjectConfigurer)
     def buildRegistry = Mock(BuildStateRegistry)
     def buildLoader = Mock(BuildLoader)
+    def modelParameters = Mock(BuildModelParameters)
     def modelListener = Mock(ModelConfigurationListener)
     def buildOperationExecutor = Mock(BuildOperationExecutor)
-    private configurer = new DefaultProjectsPreparer(projectConfigurer, modelListener, buildOperationExecutor)
+    def configurer = new DefaultProjectsPreparer(projectConfigurer, modelParameters, modelListener, buildOperationExecutor)
 
     def setup() {
         gradle.startParameter >> startParameter
@@ -55,7 +57,8 @@
         configurer.prepareProjects(gradle)
 
         then:
-        startParameter.isConfigureOnDemand() >> true
+        gradle.rootBuild >> true
+        modelParameters.configureOnDemand >> true
         1 * projectConfigurer.configure(rootProject)
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/build/DefaultBuildLifecycleControllerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/build/DefaultBuildLifecycleControllerTest.groovy
index 2fd6b37..d2840ae 100644
--- a/subprojects/core/src/test/groovy/org/gradle/internal/build/DefaultBuildLifecycleControllerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/build/DefaultBuildLifecycleControllerTest.groovy
@@ -22,7 +22,6 @@
 import org.gradle.execution.BuildWorkExecutor
 import org.gradle.execution.MultipleBuildFailures
 import org.gradle.initialization.BuildCompletionListener
-import org.gradle.initialization.BuildOptionBuildOperationProgressEventsEmitter
 import org.gradle.initialization.exception.ExceptionAnalyser
 import org.gradle.initialization.internal.InternalBuildFinishedListener
 import org.gradle.internal.service.scopes.BuildScopeServices
@@ -57,7 +56,7 @@
 
     DefaultBuildLifecycleController launcher() {
         return new DefaultBuildLifecycleController(gradleMock, buildModelController, exceptionAnalyser, buildBroadcaster,
-            buildCompletionListener, buildFinishedListener, buildExecuter, buildServices, Mock(BuildOptionBuildOperationProgressEventsEmitter))
+            buildCompletionListener, buildFinishedListener, buildExecuter, buildServices)
     }
 
     void testCanFinishBuildWhenNothingHasBeenDone() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/buildtree/BuildTreeControllerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/buildtree/BuildTreeControllerTest.groovy
index 271aef3..e7d40bd 100644
--- a/subprojects/core/src/test/groovy/org/gradle/internal/buildtree/BuildTreeControllerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/buildtree/BuildTreeControllerTest.groovy
@@ -16,7 +16,7 @@
 
 package org.gradle.internal.buildtree
 
-import org.gradle.api.internal.BuildType
+
 import org.gradle.internal.invocation.BuildAction
 import org.gradle.internal.service.DefaultServiceRegistry
 import spock.lang.Specification
@@ -27,7 +27,7 @@
     def setup() {
         def services = new DefaultServiceRegistry()
         services.add(BuildTreeActionExecutor, Stub(BuildTreeActionExecutor))
-        state = new BuildTreeController(services, BuildType.TASKS)
+        state = new BuildTreeController(services, Stub(BuildTreeModelControllerServices.Supplier))
     }
 
     def "cannot run multiple actions against a tree"() {
diff --git a/subprojects/enterprise/build.gradle.kts b/subprojects/enterprise/build.gradle.kts
index 93db4b0..3e2ab98 100644
--- a/subprojects/enterprise/build.gradle.kts
+++ b/subprojects/enterprise/build.gradle.kts
@@ -9,6 +9,7 @@
     implementation(libs.inject)
     implementation(project(":logging"))
     implementation(project(":core-api"))
+    implementation(project(":build-option"))
     implementation(project(":core"))
     implementation(project(":launcher"))
     implementation(project(":snapshots"))
diff --git a/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/legacy/LegacyGradleEnterprisePluginCheckInService.java b/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/legacy/LegacyGradleEnterprisePluginCheckInService.java
index d797322..926d805 100644
--- a/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/legacy/LegacyGradleEnterprisePluginCheckInService.java
+++ b/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/legacy/LegacyGradleEnterprisePluginCheckInService.java
@@ -19,6 +19,7 @@
 import org.gradle.StartParameter;
 import org.gradle.api.internal.BuildType;
 import org.gradle.api.internal.GradleInternal;
+import org.gradle.internal.buildtree.BuildModelParameters;
 import org.gradle.internal.enterprise.core.GradleEnterprisePluginAdapter;
 import org.gradle.internal.enterprise.core.GradleEnterprisePluginManager;
 import org.gradle.internal.scan.config.BuildScanConfig;
@@ -42,6 +43,7 @@
     private static final VersionNumber FIRST_VERSION_AWARE_OF_UNSUPPORTED = VersionNumber.parse("1.11");
 
     private final GradleInternal gradle;
+    private final BuildModelParameters buildModelParameters;
     private final GradleEnterprisePluginManager manager;
     private final BuildType buildType;
 
@@ -50,10 +52,12 @@
     @Inject
     public LegacyGradleEnterprisePluginCheckInService(
         GradleInternal gradle,
+        BuildModelParameters buildModelParameters,
         GradleEnterprisePluginManager manager,
         BuildType buildType
     ) {
         this.gradle = gradle;
+        this.buildModelParameters = buildModelParameters;
         this.manager = manager;
         this.buildType = buildType;
     }
@@ -62,7 +66,7 @@
     private String unsupportedReason(VersionNumber pluginVersion) {
         if (Boolean.getBoolean(UNSUPPORTED_TOGGLE)) {
             return UNSUPPORTED_TOGGLE_MESSAGE;
-        } else if (gradle.getStartParameter().isConfigurationCache()) {
+        } else if (buildModelParameters.isConfigurationCache()) {
             return "Build scans have been disabled due to incompatibility between your Gradle Enterprise plugin version (" + pluginVersion.toString() + ") and configuration caching. " +
                 "Please use Gradle Enterprise plugin version 3.4 or later for compatibility with configuration caching.";
         } else {
diff --git a/subprojects/enterprise/src/test/groovy/org/gradle/internal/scan/config/LegacyGradleEnterprisePluginCheckInServiceTest.groovy b/subprojects/enterprise/src/test/groovy/org/gradle/internal/scan/config/LegacyGradleEnterprisePluginCheckInServiceTest.groovy
index 2384233..ae8aca5 100644
--- a/subprojects/enterprise/src/test/groovy/org/gradle/internal/scan/config/LegacyGradleEnterprisePluginCheckInServiceTest.groovy
+++ b/subprojects/enterprise/src/test/groovy/org/gradle/internal/scan/config/LegacyGradleEnterprisePluginCheckInServiceTest.groovy
@@ -19,6 +19,7 @@
 import org.gradle.api.internal.BuildType
 import org.gradle.api.internal.GradleInternal
 import org.gradle.api.internal.StartParameterInternal
+import org.gradle.internal.buildtree.BuildModelParameters
 import org.gradle.internal.enterprise.core.GradleEnterprisePluginManager
 import org.gradle.internal.enterprise.impl.legacy.LegacyGradleEnterprisePluginCheckInService
 import org.gradle.internal.enterprise.impl.legacy.UnsupportedBuildScanPluginVersionException
@@ -105,6 +106,7 @@
 
         new LegacyGradleEnterprisePluginCheckInService(
             gradle,
+            new BuildModelParameters(false, false, false),
             new GradleEnterprisePluginManager(),
             BuildType.TASKS
         )
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/InProcessGradleExecuter.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/InProcessGradleExecuter.java
index 92aaa76..d72185c 100755
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/InProcessGradleExecuter.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/InProcessGradleExecuter.java
@@ -64,7 +64,6 @@
 import org.gradle.launcher.Main;
 import org.gradle.launcher.cli.Parameters;
 import org.gradle.launcher.cli.ParametersConverter;
-import org.gradle.launcher.cli.action.ExecuteBuildAction;
 import org.gradle.launcher.exec.BuildActionExecuter;
 import org.gradle.launcher.exec.BuildActionParameters;
 import org.gradle.launcher.exec.BuildActionResult;
@@ -73,6 +72,7 @@
 import org.gradle.test.fixtures.file.TestDirectoryProvider;
 import org.gradle.test.fixtures.file.TestFile;
 import org.gradle.testfixtures.internal.NativeServicesTestFixture;
+import org.gradle.tooling.internal.provider.action.ExecuteBuildAction;
 import org.gradle.tooling.internal.provider.serialization.DeserializeMap;
 import org.gradle.tooling.internal.provider.serialization.PayloadClassLoaderRegistry;
 import org.gradle.tooling.internal.provider.serialization.PayloadSerializer;
diff --git a/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoCachingIntegrationTest.groovy b/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoCachingIntegrationTest.groovy
index 2f2157c..9f78740 100644
--- a/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoCachingIntegrationTest.groovy
+++ b/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoCachingIntegrationTest.groovy
@@ -44,10 +44,10 @@
 
     def "jacoco file results are cached"() {
         when:
-        withBuildCache().run "test", "jacocoTestReport"
+        withBuildCache().run "test", "jacocoTestReport", "jacocoTestCoverageVerification"
         def snapshot = reportFile.snapshot()
         then:
-        executedAndNotSkipped ":test", ":jacocoTestReport"
+        executedAndNotSkipped ":test", ":jacocoTestReport", ":jacocoTestCoverageVerification"
         reportFile.assertIsFile()
 
         when:
@@ -56,9 +56,9 @@
         reportFile.assertDoesNotExist()
 
         when:
-        withBuildCache().run "jacocoTestReport"
+        withBuildCache().run "jacocoTestReport", "jacocoTestCoverageVerification"
         then:
-        skipped ":test", ":jacocoTestReport"
+        skipped ":test", ":jacocoTestReport", ":jacocoTestCoverageVerification"
         reportFile.assertContentsHaveNotChangedSince(snapshot)
     }
 
diff --git a/subprojects/jacoco/src/main/java/org/gradle/testing/jacoco/tasks/JacocoCoverageVerification.java b/subprojects/jacoco/src/main/java/org/gradle/testing/jacoco/tasks/JacocoCoverageVerification.java
index 4f5e027..a70b603 100644
--- a/subprojects/jacoco/src/main/java/org/gradle/testing/jacoco/tasks/JacocoCoverageVerification.java
+++ b/subprojects/jacoco/src/main/java/org/gradle/testing/jacoco/tasks/JacocoCoverageVerification.java
@@ -18,7 +18,9 @@
 
 import org.gradle.api.Action;
 import org.gradle.api.GradleException;
+import org.gradle.api.tasks.CacheableTask;
 import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.OutputFile;
 import org.gradle.api.tasks.TaskAction;
 import org.gradle.internal.jacoco.AntJacocoCheck;
 import org.gradle.internal.jacoco.JacocoCheckResult;
@@ -27,6 +29,7 @@
 import org.gradle.testing.jacoco.tasks.rules.JacocoViolationRulesContainer;
 
 import java.io.File;
+import java.io.IOException;
 
 /**
  * Task for verifying code coverage metrics. Fails the task if violations are detected based on specified rules.
@@ -35,6 +38,7 @@
  *
  * @since 3.4
  */
+@CacheableTask
 public class JacocoCoverageVerification extends JacocoReportBase {
 
     private final JacocoViolationRulesContainer violationRules;
@@ -55,6 +59,14 @@
         return violationRules;
     }
 
+    /**
+     * For internal use only. This property exists, because only tasks with outputs can be up-to-date and cached.
+     */
+    @OutputFile
+    protected File getDummyOutputFile() {
+        return new File(getTemporaryDir(), "success.txt");
+    }
+
     private final String projectName = getProject().getName();
 
     /**
@@ -66,7 +78,7 @@
     }
 
     @TaskAction
-    public void check() {
+    public void check() throws IOException {
         JacocoCheckResult checkResult = new AntJacocoCheck(getAntBuilder()).execute(
             getJacocoClasspath(),
             projectName,
@@ -78,6 +90,8 @@
 
         if (!checkResult.isSuccess()) {
             throw new GradleException(checkResult.getFailureMessage());
+        } else {
+            getDummyOutputFile().createNewFile();
         }
     }
 }
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/swift/AbstractSwiftIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/swift/AbstractSwiftIntegrationTest.groovy
index 7c3b4c5..bd2c8c3 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/swift/AbstractSwiftIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/swift/AbstractSwiftIntegrationTest.groovy
@@ -27,7 +27,6 @@
 
 @RequiresInstalledToolChain(ToolChainRequirement.SWIFTC)
 abstract class AbstractSwiftIntegrationTest extends AbstractSwiftComponentIntegrationTest {
-
     def "skip assemble tasks when no source"() {
         given:
         makeSingleProject()
diff --git a/subprojects/language-native/src/testFixtures/groovy/org/gradle/language/swift/AbstractSwiftComponentIntegrationTest.groovy b/subprojects/language-native/src/testFixtures/groovy/org/gradle/language/swift/AbstractSwiftComponentIntegrationTest.groovy
index 55ccde0..2a37d9b 100644
--- a/subprojects/language-native/src/testFixtures/groovy/org/gradle/language/swift/AbstractSwiftComponentIntegrationTest.groovy
+++ b/subprojects/language-native/src/testFixtures/groovy/org/gradle/language/swift/AbstractSwiftComponentIntegrationTest.groovy
@@ -24,7 +24,6 @@
 import org.gradle.nativeplatform.fixtures.app.SourceElement
 import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
 import org.hamcrest.CoreMatchers
-import spock.lang.Ignore
 
 @RequiresInstalledToolChain(ToolChainRequirement.SWIFTC)
 abstract class AbstractSwiftComponentIntegrationTest extends AbstractNativeLanguageComponentIntegrationTest {
@@ -89,8 +88,12 @@
     }
 
     @RequiresInstalledToolChain(ToolChainRequirement.SWIFTC_4)
-    @ToBeFixedForConfigurationCache
-    @Ignore('https://github.com/gradle/gradle-private/issues/3369')
+    @ToBeFixedForConfigurationCache(bottomSpecs = [
+        'SwiftXCTestComponentWithoutComponentIntegrationTest',
+        'SwiftXCTestComponentWithBothLibraryLinkageIntegrationTest',
+        'SwiftXCTestComponentWithSharedLibraryLinkageIntegrationTest',
+        'SwiftXCTestComponentWithStaticLibraryLinkageIntegrationTest',
+        'SwiftXCTestComponentWithApplicationIntegrationTest'])
     def "can build Swift 3 source code on Swift 4 compiler"() {
         given:
         makeSingleProject()
@@ -295,8 +298,12 @@
     }
 
     @RequiresInstalledToolChain(ToolChainRequirement.SWIFTC_4)
-    @ToBeFixedForConfigurationCache
-    @Ignore("https://github.com/gradle/gradle-private/issues/3369")
+    @ToBeFixedForConfigurationCache(bottomSpecs = [
+        'SwiftXCTestComponentWithoutComponentIntegrationTest',
+        'SwiftXCTestComponentWithBothLibraryLinkageIntegrationTest',
+        'SwiftXCTestComponentWithSharedLibraryLinkageIntegrationTest',
+        'SwiftXCTestComponentWithStaticLibraryLinkageIntegrationTest',
+        'SwiftXCTestComponentWithApplicationIntegrationTest'])
     def "can compile Swift 4 component on Swift 4 compiler"() {
         given:
         makeSingleProject()
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/RunBuildAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/RunBuildAction.java
index 27cd325..f2615d6 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/RunBuildAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/RunBuildAction.java
@@ -26,10 +26,10 @@
 import org.gradle.internal.concurrent.Stoppable;
 import org.gradle.internal.nativeintegration.console.ConsoleDetector;
 import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.launcher.cli.action.ExecuteBuildAction;
 import org.gradle.launcher.exec.BuildActionExecuter;
 import org.gradle.launcher.exec.BuildActionParameters;
 import org.gradle.launcher.exec.BuildActionResult;
+import org.gradle.tooling.internal.provider.action.ExecuteBuildAction;
 
 public class RunBuildAction implements Runnable {
     private final BuildActionExecuter<BuildActionParameters, BuildRequestContext> executer;
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/action/BuildActionSerializer.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/action/BuildActionSerializer.java
deleted file mode 100644
index b96505b..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/action/BuildActionSerializer.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright 2018 the original author or authors.
- *
- * Licensed 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.gradle.launcher.cli.action;
-
-import org.gradle.TaskExecutionRequest;
-import org.gradle.api.artifacts.verification.DependencyVerificationMode;
-import org.gradle.api.internal.StartParameterInternal;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.api.logging.configuration.ConsoleOutput;
-import org.gradle.api.logging.configuration.ShowStacktrace;
-import org.gradle.api.logging.configuration.WarningMode;
-import org.gradle.initialization.StartParameterBuildOptions.ConfigurationCacheProblemsOption;
-import org.gradle.internal.DefaultTaskExecutionRequest;
-import org.gradle.internal.invocation.BuildAction;
-import org.gradle.internal.serialize.BaseSerializerFactory;
-import org.gradle.internal.serialize.Decoder;
-import org.gradle.internal.serialize.DefaultSerializerRegistry;
-import org.gradle.internal.serialize.Encoder;
-import org.gradle.internal.serialize.ListSerializer;
-import org.gradle.internal.serialize.Serializer;
-import org.gradle.internal.serialize.SetSerializer;
-import org.gradle.internal.watch.vfs.WatchMode;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-import static org.gradle.internal.serialize.BaseSerializerFactory.FILE_SERIALIZER;
-import static org.gradle.internal.serialize.BaseSerializerFactory.NO_NULL_STRING_MAP_SERIALIZER;
-
-public class BuildActionSerializer {
-    public static Serializer<BuildAction> create() {
-        DefaultSerializerRegistry registry = new DefaultSerializerRegistry();
-        registry.register(ExecuteBuildAction.class, new ExecuteBuildActionSerializer());
-        // Use Java serialization for everything else
-        registry.useJavaSerialization(BuildAction.class);
-        return registry.build(BuildAction.class);
-    }
-
-    private static class ExecuteBuildActionSerializer implements Serializer<ExecuteBuildAction> {
-        private final Serializer<LogLevel> logLevelSerializer;
-        private final Serializer<ShowStacktrace> showStacktraceSerializer;
-        private final Serializer<ConsoleOutput> consoleOutputSerializer;
-        private final Serializer<WarningMode> warningModeSerializer;
-        private final Serializer<File> nullableFileSerializer = new NullableFileSerializer();
-        private final Serializer<List<String>> stringListSerializer = new ListSerializer<>(BaseSerializerFactory.STRING_SERIALIZER);
-        private final Serializer<List<File>> fileListSerializer = new ListSerializer<>(BaseSerializerFactory.FILE_SERIALIZER);
-        private final Serializer<Set<String>> stringSetSerializer = new SetSerializer<>(BaseSerializerFactory.STRING_SERIALIZER);
-
-        ExecuteBuildActionSerializer() {
-            BaseSerializerFactory serializerFactory = new BaseSerializerFactory();
-            logLevelSerializer = serializerFactory.getSerializerFor(LogLevel.class);
-            showStacktraceSerializer = serializerFactory.getSerializerFor(ShowStacktrace.class);
-            consoleOutputSerializer = serializerFactory.getSerializerFor(ConsoleOutput.class);
-            warningModeSerializer = serializerFactory.getSerializerFor(WarningMode.class);
-        }
-
-        @Override
-        public void write(Encoder encoder, ExecuteBuildAction action) throws Exception {
-            StartParameterInternal startParameter = action.getStartParameter();
-
-            // Log configuration
-            logLevelSerializer.write(encoder, startParameter.getLogLevel());
-            showStacktraceSerializer.write(encoder, startParameter.getShowStacktrace());
-            consoleOutputSerializer.write(encoder, startParameter.getConsoleOutput());
-            warningModeSerializer.write(encoder, startParameter.getWarningMode());
-
-            // Parallel configuration
-            encoder.writeBoolean(startParameter.isParallelProjectExecutionEnabled());
-            encoder.writeSmallInt(startParameter.getMaxWorkerCount());
-
-            // Tasks
-            writeTaskRequests(encoder, startParameter.getTaskRequests());
-            stringSetSerializer.write(encoder, startParameter.getExcludedTaskNames());
-
-            // Layout
-            @SuppressWarnings("deprecation")
-            File customBuildFile = startParameter.getBuildFile();
-            nullableFileSerializer.write(encoder, customBuildFile);
-            nullableFileSerializer.write(encoder, startParameter.getProjectDir());
-            @SuppressWarnings("deprecation")
-            File customSettingsFile = startParameter.getSettingsFile();
-            nullableFileSerializer.write(encoder, customSettingsFile);
-            FILE_SERIALIZER.write(encoder, startParameter.getCurrentDir());
-            FILE_SERIALIZER.write(encoder, startParameter.getGradleUserHomeDir());
-            nullableFileSerializer.write(encoder, startParameter.getGradleHomeDir());
-            nullableFileSerializer.write(encoder, startParameter.getProjectCacheDir());
-            fileListSerializer.write(encoder, startParameter.getIncludedBuilds());
-
-            // Other stuff
-            NO_NULL_STRING_MAP_SERIALIZER.write(encoder, startParameter.getProjectProperties());
-            NO_NULL_STRING_MAP_SERIALIZER.write(encoder, startParameter.getSystemPropertiesArgs());
-            fileListSerializer.write(encoder, startParameter.getInitScripts());
-            stringListSerializer.write(encoder, startParameter.getLockedDependenciesToUpdate());
-
-            // Flags
-            encoder.writeBoolean(startParameter.isBuildProjectDependencies());
-            encoder.writeBoolean(startParameter.isDryRun());
-            encoder.writeBoolean(startParameter.isRerunTasks());
-            encoder.writeBoolean(startParameter.isProfile());
-            encoder.writeBoolean(startParameter.isContinueOnFailure());
-            encoder.writeBoolean(startParameter.isOffline());
-            encoder.writeBoolean(startParameter.isRefreshDependencies());
-            encoder.writeBoolean(startParameter.isBuildCacheEnabled());
-            encoder.writeBoolean(startParameter.isBuildCacheDebugLogging());
-            encoder.writeString(startParameter.getWatchFileSystemMode().name());
-            encoder.writeBoolean(startParameter.isWatchFileSystemDebugLogging());
-            encoder.writeBoolean(startParameter.isVfsVerboseLogging());
-            encoder.writeBoolean(startParameter.isConfigurationCache());
-            encoder.writeString(startParameter.getConfigurationCacheProblems().name());
-            encoder.writeSmallInt(startParameter.getConfigurationCacheMaxProblems());
-            encoder.writeBoolean(startParameter.isConfigurationCacheRecreateCache());
-            encoder.writeBoolean(startParameter.isConfigurationCacheQuiet());
-            encoder.writeBoolean(startParameter.isConfigureOnDemand());
-            encoder.writeBoolean(startParameter.isContinuous());
-            encoder.writeBoolean(startParameter.isBuildScan());
-            encoder.writeBoolean(startParameter.isNoBuildScan());
-            encoder.writeBoolean(startParameter.isWriteDependencyLocks());
-            stringListSerializer.write(encoder, startParameter.getWriteDependencyVerifications());
-            encoder.writeString(startParameter.getDependencyVerificationMode().name());
-            encoder.writeBoolean(startParameter.isRefreshKeys());
-            encoder.writeBoolean(startParameter.isExportKeys());
-        }
-
-        private void writeTaskRequests(Encoder encoder, List<TaskExecutionRequest> taskRequests) throws Exception {
-            encoder.writeSmallInt(taskRequests.size());
-            for (TaskExecutionRequest taskRequest : taskRequests) {
-                if (!(taskRequest instanceof DefaultTaskExecutionRequest)) {
-                    // Only handle the command line for now
-                    throw new UnsupportedOperationException();
-                }
-                DefaultTaskExecutionRequest request = (DefaultTaskExecutionRequest) taskRequests.get(0);
-                stringListSerializer.write(encoder, request.getArgs());
-            }
-        }
-
-        @SuppressWarnings("deprecation") // StartParameter.setBuildFile and StartParameter.setSettingsFile
-        @Override
-        public ExecuteBuildAction read(Decoder decoder) throws Exception {
-            StartParameterInternal startParameter = new StartParameterInternal();
-
-            // Logging configuration
-            startParameter.setLogLevel(logLevelSerializer.read(decoder));
-            startParameter.setShowStacktrace(showStacktraceSerializer.read(decoder));
-            startParameter.setConsoleOutput(consoleOutputSerializer.read(decoder));
-            startParameter.setWarningMode(warningModeSerializer.read(decoder));
-
-            // Parallel configuration
-            startParameter.setParallelProjectExecutionEnabled(decoder.readBoolean());
-            startParameter.setMaxWorkerCount(decoder.readSmallInt());
-
-            // Tasks
-            startParameter.setTaskRequests(readTaskRequests(decoder));
-            startParameter.setExcludedTaskNames(stringSetSerializer.read(decoder));
-
-            // Layout
-            startParameter.setBuildFile(nullableFileSerializer.read(decoder));
-            startParameter.setProjectDir(nullableFileSerializer.read(decoder));
-            startParameter.setSettingsFile(nullableFileSerializer.read(decoder));
-            startParameter.setCurrentDir(FILE_SERIALIZER.read(decoder));
-            startParameter.setGradleUserHomeDir(FILE_SERIALIZER.read(decoder));
-            startParameter.setGradleHomeDir(nullableFileSerializer.read(decoder));
-            startParameter.setProjectCacheDir(nullableFileSerializer.read(decoder));
-            startParameter.setIncludedBuilds(fileListSerializer.read(decoder));
-
-            // Other stuff
-            startParameter.setProjectProperties(NO_NULL_STRING_MAP_SERIALIZER.read(decoder));
-            startParameter.setSystemPropertiesArgs(NO_NULL_STRING_MAP_SERIALIZER.read(decoder));
-            startParameter.setInitScripts(fileListSerializer.read(decoder));
-            startParameter.setLockedDependenciesToUpdate(stringListSerializer.read(decoder));
-
-            // Flags
-            startParameter.setBuildProjectDependencies(decoder.readBoolean());
-            startParameter.setDryRun(decoder.readBoolean());
-            startParameter.setRerunTasks(decoder.readBoolean());
-            startParameter.setProfile(decoder.readBoolean());
-            startParameter.setContinueOnFailure(decoder.readBoolean());
-            startParameter.setOffline(decoder.readBoolean());
-            startParameter.setRefreshDependencies(decoder.readBoolean());
-            startParameter.setBuildCacheEnabled(decoder.readBoolean());
-            startParameter.setBuildCacheDebugLogging(decoder.readBoolean());
-            startParameter.setWatchFileSystemMode(WatchMode.valueOf(decoder.readString()));
-            startParameter.setWatchFileSystemDebugLogging(decoder.readBoolean());
-            startParameter.setVfsVerboseLogging(decoder.readBoolean());
-            startParameter.setConfigurationCache(decoder.readBoolean());
-            startParameter.setConfigurationCacheProblems(ConfigurationCacheProblemsOption.Value.valueOf(decoder.readString()));
-            startParameter.setConfigurationCacheMaxProblems(decoder.readSmallInt());
-            startParameter.setConfigurationCacheRecreateCache(decoder.readBoolean());
-            startParameter.setConfigurationCacheQuiet(decoder.readBoolean());
-            startParameter.setConfigureOnDemand(decoder.readBoolean());
-            startParameter.setContinuous(decoder.readBoolean());
-            startParameter.setBuildScan(decoder.readBoolean());
-            startParameter.setNoBuildScan(decoder.readBoolean());
-            startParameter.setWriteDependencyLocks(decoder.readBoolean());
-            List<String> checksums = stringListSerializer.read(decoder);
-            if (!checksums.isEmpty()) {
-                startParameter.setWriteDependencyVerifications(checksums);
-            }
-            startParameter.setDependencyVerificationMode(DependencyVerificationMode.valueOf(decoder.readString()));
-            startParameter.setRefreshKeys(decoder.readBoolean());
-            startParameter.setExportKeys(decoder.readBoolean());
-
-            return new ExecuteBuildAction(startParameter);
-        }
-
-        private List<TaskExecutionRequest> readTaskRequests(Decoder decoder) throws Exception {
-            int requestCount = decoder.readSmallInt();
-            List<TaskExecutionRequest> taskExecutionRequests = new ArrayList<>(requestCount);
-            for (int i = 0; i < requestCount; i++) {
-                taskExecutionRequests.add(new DefaultTaskExecutionRequest(stringListSerializer.read(decoder)));
-            }
-            return taskExecutionRequests;
-        }
-    }
-
-    private static class NullableFileSerializer implements Serializer<File> {
-        @Override
-        public void write(Encoder encoder, File value) throws Exception {
-            if (value == null) {
-                encoder.writeBoolean(false);
-            } else {
-                encoder.writeBoolean(true);
-                encoder.writeString(value.getPath());
-            }
-        }
-
-        @Override
-        public File read(Decoder decoder) throws Exception {
-            if (decoder.readBoolean()) {
-                return new File(decoder.readString());
-            }
-            return null;
-        }
-    }
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientGlobalServices.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientGlobalServices.java
index 5d5760f..8e2f513 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientGlobalServices.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientGlobalServices.java
@@ -21,7 +21,7 @@
 import org.gradle.internal.jvm.inspection.JvmVersionDetector;
 import org.gradle.internal.serialize.Serializer;
 import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.launcher.cli.action.BuildActionSerializer;
+import org.gradle.tooling.internal.provider.action.BuildActionSerializer;
 
 /**
  * Global services shared by all Gradle daemon clients in a given process.
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/DaemonMessageSerializer.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/DaemonMessageSerializer.java
index c95ceda..61c237b 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/DaemonMessageSerializer.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/DaemonMessageSerializer.java
@@ -93,7 +93,7 @@
         registry.register(UserInputRequestEvent.class, new UserInputRequestEventSerializer());
         registry.register(PromptOutputEvent.class, new PromptOutputEventSerializer());
         registry.register(UserInputResumeEvent.class, new UserInputResumeEventSerializer());
-        registry.register(StyledTextOutputEvent.class, new StyledTextOutputEventSerializer(logLevelSerializer, new ListSerializer<StyledTextOutputEvent.Span>(new SpanSerializer(factory.getSerializerFor(StyledTextOutput.Style.class)))));
+        registry.register(StyledTextOutputEvent.class, new StyledTextOutputEventSerializer(logLevelSerializer, new ListSerializer<>(new SpanSerializer(factory.getSerializerFor(StyledTextOutput.Style.class)))));
         registry.register(ProgressStartEvent.class, new ProgressStartEventSerializer());
         registry.register(ProgressCompleteEvent.class, new ProgressCompleteEventSerializer());
         registry.register(ProgressEvent.class, new ProgressEventSerializer());
@@ -107,7 +107,7 @@
     }
 
     private static class SuccessSerializer implements Serializer<Success> {
-        private final Serializer<Object> javaSerializer = new DefaultSerializer<Object>();
+        private final Serializer<Object> javaSerializer = new DefaultSerializer<>();
         private final Serializer<SerializedPayload> payloadSerializer = new SerializedPayloadSerializer();
 
         @Override
@@ -151,7 +151,7 @@
                 case 0:
                     return new Success(null);
                 case 1:
-                    return new Success(BuildActionResult.of(new SerializedPayload(null, Collections.<byte[]>emptyList())));
+                    return new Success(BuildActionResult.of(new SerializedPayload(null, Collections.emptyList())));
                 case 2:
                     SerializedPayload result = payloadSerializer.read(decoder);
                     return new Success(BuildActionResult.of(result));
@@ -190,7 +190,7 @@
     }
 
     private static class BuildEventSerializer implements Serializer<BuildEvent> {
-        private final Serializer<Object> payloadSerializer = new DefaultSerializer<Object>();
+        private final Serializer<Object> payloadSerializer = new DefaultSerializer<>();
 
         @Override
         public void write(Encoder encoder, BuildEvent buildEvent) throws Exception {
@@ -284,7 +284,7 @@
 
         BuildActionParametersSerializer() {
             logLevelSerializer = new BaseSerializerFactory().getSerializerFor(LogLevel.class);
-            classPathSerializer = new ListSerializer<File>(FILE_SERIALIZER);
+            classPathSerializer = new ListSerializer<>(FILE_SERIALIZER);
         }
 
         @Override
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonServices.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonServices.java
index b0fa8d7..b143c0d 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonServices.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonServices.java
@@ -31,7 +31,6 @@
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.scopes.GlobalScopeServices;
 import org.gradle.internal.service.scopes.GradleUserHomeScopeServiceRegistry;
-import org.gradle.launcher.cli.action.BuildActionSerializer;
 import org.gradle.launcher.daemon.configuration.DaemonServerConfiguration;
 import org.gradle.launcher.daemon.context.DaemonContext;
 import org.gradle.launcher.daemon.context.DaemonContextBuilder;
@@ -65,6 +64,7 @@
 import org.gradle.launcher.daemon.server.scaninfo.DefaultDaemonScanInfo;
 import org.gradle.launcher.daemon.server.stats.DaemonRunningStats;
 import org.gradle.launcher.exec.BuildExecuter;
+import org.gradle.tooling.internal.provider.action.BuildActionSerializer;
 
 import java.io.File;
 import java.util.UUID;
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/BuildTreeScopeLifecycleBuildActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/BuildTreeScopeLifecycleBuildActionExecuter.java
index 3faa585..0b97080 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/BuildTreeScopeLifecycleBuildActionExecuter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/BuildTreeScopeLifecycleBuildActionExecuter.java
@@ -16,27 +16,27 @@
 
 package org.gradle.launcher.exec;
 
-import org.gradle.api.internal.BuildType;
-import org.gradle.internal.buildtree.BuildTreeController;
-import org.gradle.internal.invocation.BuildAction;
 import org.gradle.internal.buildtree.BuildActionRunner;
-import org.gradle.internal.session.BuildSessionContext;
+import org.gradle.internal.buildtree.BuildTreeController;
+import org.gradle.internal.buildtree.BuildTreeModelControllerServices;
+import org.gradle.internal.invocation.BuildAction;
 import org.gradle.internal.session.BuildSessionActionExecutor;
+import org.gradle.internal.session.BuildSessionContext;
 
 /**
  * A {@link BuildActionExecuter} responsible for establishing the build tree for a single invocation of a {@link BuildAction}.
  */
 public class BuildTreeScopeLifecycleBuildActionExecuter implements BuildSessionActionExecutor {
+    private final BuildTreeModelControllerServices buildTreeModelControllerServices;
+
+    public BuildTreeScopeLifecycleBuildActionExecuter(BuildTreeModelControllerServices buildTreeModelControllerServices) {
+        this.buildTreeModelControllerServices = buildTreeModelControllerServices;
+    }
+
     @Override
     public BuildActionRunner.Result execute(BuildAction action, BuildSessionContext buildSession) {
-        BuildType buildType = action.isRunTasks() ? BuildType.TASKS : BuildType.MODEL;
-        if (action.isCreateModel()) {
-            // When creating a model, do not use configure on demand or configuration cache
-            action.getStartParameter().setConfigureOnDemand(false);
-            action.getStartParameter().setConfigurationCache(false);
-        }
-
-        try (BuildTreeController buildTree = new BuildTreeController(buildSession.getServices(), buildType)) {
+        BuildTreeModelControllerServices.Supplier modelServices = buildTreeModelControllerServices.servicesForBuildTree(action.isRunTasks(), action.isCreateModel(), action.getStartParameter());
+        try (BuildTreeController buildTree = new BuildTreeController(buildSession.getServices(), modelServices)) {
             return buildTree.run(context -> context.execute(action));
         }
     }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/RunAsBuildOperationBuildActionRunner.java b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/RunAsBuildOperationBuildActionRunner.java
index 3c7febd..712981c 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/RunAsBuildOperationBuildActionRunner.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/RunAsBuildOperationBuildActionRunner.java
@@ -17,9 +17,10 @@
 package org.gradle.launcher.exec;
 
 import org.gradle.composite.internal.IncludedBuildControllers;
-import org.gradle.internal.invocation.BuildAction;
+import org.gradle.initialization.BuildOptionBuildOperationProgressEventsEmitter;
 import org.gradle.internal.buildtree.BuildActionRunner;
 import org.gradle.internal.buildtree.BuildTreeLifecycleController;
+import org.gradle.internal.invocation.BuildAction;
 import org.gradle.internal.operations.BuildOperationContext;
 import org.gradle.internal.operations.BuildOperationDescriptor;
 import org.gradle.internal.operations.BuildOperationExecutor;
@@ -46,6 +47,7 @@
             public Result call(BuildOperationContext context) {
                 buildController.getGradle().getServices().get(IncludedBuildControllers.class).rootBuildOperationStarted();
                 buildController.getGradle().getServices().get(LoggingBuildOperationProgressBroadcaster.class).rootBuildOperationStarted();
+                buildController.getGradle().getServices().get(BuildOptionBuildOperationProgressEventsEmitter.class).emit();
                 Result result = delegate.run(action, buildController);
                 context.setResult(RESULT);
                 if (result.getBuildFailure() != null) {
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ExecuteBuildActionRunner.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ExecuteBuildActionRunner.java
index 8797cd8..a05c7c2 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ExecuteBuildActionRunner.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ExecuteBuildActionRunner.java
@@ -16,10 +16,10 @@
 
 package org.gradle.tooling.internal.provider;
 
-import org.gradle.internal.invocation.BuildAction;
 import org.gradle.internal.buildtree.BuildActionRunner;
 import org.gradle.internal.buildtree.BuildTreeLifecycleController;
-import org.gradle.launcher.cli.action.ExecuteBuildAction;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.tooling.internal.provider.action.ExecuteBuildAction;
 
 public class ExecuteBuildActionRunner implements BuildActionRunner {
     @Override
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LauncherServices.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LauncherServices.java
index 9c50180..0a77b74 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LauncherServices.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LauncherServices.java
@@ -25,13 +25,15 @@
 import org.gradle.internal.build.BuildStateRegistry;
 import org.gradle.internal.build.event.BuildEventListenerFactory;
 import org.gradle.internal.buildevents.BuildStartedTime;
+import org.gradle.internal.buildtree.BuildActionRunner;
+import org.gradle.internal.buildtree.BuildTreeActionExecutor;
+import org.gradle.internal.buildtree.BuildTreeModelControllerServices;
 import org.gradle.internal.classpath.CachedClasspathTransformer;
 import org.gradle.internal.concurrent.ExecutorFactory;
 import org.gradle.internal.event.ListenerManager;
 import org.gradle.internal.filewatch.DefaultFileSystemChangeWaiterFactory;
 import org.gradle.internal.filewatch.FileSystemChangeWaiterFactory;
 import org.gradle.internal.filewatch.FileWatcherFactory;
-import org.gradle.internal.buildtree.BuildActionRunner;
 import org.gradle.internal.logging.LoggingManagerInternal;
 import org.gradle.internal.logging.text.StyledTextOutputFactory;
 import org.gradle.internal.operations.BuildOperationListenerManager;
@@ -47,7 +49,6 @@
 import org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner;
 import org.gradle.launcher.exec.BuildExecuter;
 import org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner;
-import org.gradle.internal.buildtree.BuildTreeActionExecutor;
 import org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter;
 import org.gradle.launcher.exec.ChainingBuildActionRunner;
 import org.gradle.launcher.exec.InProcessBuildActionExecuter;
@@ -143,16 +144,17 @@
                                                         DeploymentRegistryInternal deploymentRegistry,
                                                         BuildEventConsumer eventConsumer,
                                                         BuildStartedTime buildStartedTime,
-                                                        Clock clock
+                                                        Clock clock,
+                                                        BuildTreeModelControllerServices buildModelServices
         ) {
             return new SubscribableBuildActionExecuter(listenerManager, buildOperationListenerManager, listenerFactory, eventConsumer,
                 new ContinuousBuildActionExecuter(fileSystemChangeWaiterFactory, inputsListeners, styledTextOutputFactory, executorFactory, requestMetaData, cancellationToken, deploymentRegistry, listenerManager, buildStartedTime, clock,
-                    new BuildTreeScopeLifecycleBuildActionExecuter()));
+                    new BuildTreeScopeLifecycleBuildActionExecuter(buildModelServices)));
         }
     }
 
     static class ToolingBuildTreeScopeServices {
-        BuildTreeActionExecutor createActionExecuter(List<BuildActionRunner> buildActionRunners,
+        BuildTreeActionExecutor createActionExecutor(List<BuildActionRunner> buildActionRunners,
                                                      StyledTextOutputFactory styledTextOutputFactory,
                                                      BuildStateRegistry buildStateRegistry,
                                                      BuildOperationNotificationValve buildOperationNotificationValve,
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ProviderConnection.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ProviderConnection.java
index 1bb5e03..7ca4c35 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ProviderConnection.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ProviderConnection.java
@@ -68,6 +68,10 @@
 import org.gradle.tooling.internal.protocol.PhasedActionResultListener;
 import org.gradle.tooling.internal.protocol.events.InternalProgressEvent;
 import org.gradle.tooling.internal.protocol.test.InternalTestExecutionException;
+import org.gradle.tooling.internal.provider.action.BuildModelAction;
+import org.gradle.tooling.internal.provider.action.ClientProvidedBuildAction;
+import org.gradle.tooling.internal.provider.action.ClientProvidedPhasedAction;
+import org.gradle.tooling.internal.provider.action.TestExecutionRequestAction;
 import org.gradle.tooling.internal.provider.connection.ProviderConnectionParameters;
 import org.gradle.tooling.internal.provider.connection.ProviderOperationParameters;
 import org.gradle.tooling.internal.provider.serialization.PayloadSerializer;
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/SubscribableBuildActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/SubscribableBuildActionExecuter.java
index 84f0374..6c479c6 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/SubscribableBuildActionExecuter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/SubscribableBuildActionExecuter.java
@@ -19,13 +19,14 @@
 import org.gradle.initialization.BuildEventConsumer;
 import org.gradle.internal.build.event.BuildEventListenerFactory;
 import org.gradle.internal.build.event.BuildEventSubscriptions;
+import org.gradle.internal.buildtree.BuildActionRunner;
 import org.gradle.internal.event.ListenerManager;
 import org.gradle.internal.invocation.BuildAction;
-import org.gradle.internal.buildtree.BuildActionRunner;
 import org.gradle.internal.operations.BuildOperationListener;
 import org.gradle.internal.operations.BuildOperationListenerManager;
-import org.gradle.internal.session.BuildSessionContext;
 import org.gradle.internal.session.BuildSessionActionExecutor;
+import org.gradle.internal.session.BuildSessionContext;
+import org.gradle.tooling.internal.provider.action.SubscribableBuildAction;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/BuildActionSerializer.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/BuildActionSerializer.java
new file mode 100644
index 0000000..efc84fd
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/BuildActionSerializer.java
@@ -0,0 +1,451 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed 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.gradle.tooling.internal.provider.action;
+
+import org.gradle.TaskExecutionRequest;
+import org.gradle.api.artifacts.verification.DependencyVerificationMode;
+import org.gradle.api.internal.StartParameterInternal;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.configuration.ConsoleOutput;
+import org.gradle.api.logging.configuration.ShowStacktrace;
+import org.gradle.api.logging.configuration.WarningMode;
+import org.gradle.initialization.StartParameterBuildOptions.ConfigurationCacheProblemsOption;
+import org.gradle.internal.DefaultTaskExecutionRequest;
+import org.gradle.internal.build.event.BuildEventSubscriptions;
+import org.gradle.internal.buildoption.BuildOption;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.internal.serialize.BaseSerializerFactory;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.DefaultSerializer;
+import org.gradle.internal.serialize.DefaultSerializerRegistry;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.ListSerializer;
+import org.gradle.internal.serialize.Serializer;
+import org.gradle.internal.serialize.SetSerializer;
+import org.gradle.internal.watch.vfs.WatchMode;
+import org.gradle.tooling.events.OperationType;
+import org.gradle.tooling.internal.protocol.events.InternalTestDescriptor;
+import org.gradle.tooling.internal.protocol.test.InternalDebugOptions;
+import org.gradle.tooling.internal.protocol.test.InternalJvmTestRequest;
+import org.gradle.tooling.internal.provider.serialization.SerializedPayload;
+import org.gradle.tooling.internal.provider.serialization.SerializedPayloadSerializer;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.gradle.internal.serialize.BaseSerializerFactory.FILE_SERIALIZER;
+import static org.gradle.internal.serialize.BaseSerializerFactory.NO_NULL_STRING_MAP_SERIALIZER;
+
+public class BuildActionSerializer {
+    public static Serializer<BuildAction> create() {
+        DefaultSerializerRegistry registry = new DefaultSerializerRegistry();
+        registry.register(ExecuteBuildAction.class, new ExecuteBuildActionSerializer());
+        registry.register(BuildModelAction.class, new BuildModelActionSerializer());
+        registry.register(ClientProvidedBuildAction.class, new ClientProvidedBuildActionSerializer());
+        registry.register(ClientProvidedPhasedAction.class, new ClientProvidedPhasedActionSerializer());
+        registry.register(TestExecutionRequestAction.class, new TestExecutionRequestActionSerializer());
+        return registry.build(BuildAction.class);
+    }
+
+    private static class StartParameterSerializer implements Serializer<StartParameterInternal> {
+        private final Serializer<LogLevel> logLevelSerializer;
+        private final Serializer<ShowStacktrace> showStacktraceSerializer;
+        private final Serializer<ConsoleOutput> consoleOutputSerializer;
+        private final Serializer<WarningMode> warningModeSerializer;
+        private final Serializer<File> nullableFileSerializer = new NullableFileSerializer();
+        private final Serializer<List<String>> stringListSerializer = new ListSerializer<>(BaseSerializerFactory.STRING_SERIALIZER);
+        private final Serializer<List<File>> fileListSerializer = new ListSerializer<>(BaseSerializerFactory.FILE_SERIALIZER);
+        private final Serializer<Set<String>> stringSetSerializer = new SetSerializer<>(BaseSerializerFactory.STRING_SERIALIZER);
+        private final Serializer<BuildOption.Value<Boolean>> valueSerializer = new ValueSerializer();
+
+        StartParameterSerializer() {
+            BaseSerializerFactory serializerFactory = new BaseSerializerFactory();
+            logLevelSerializer = serializerFactory.getSerializerFor(LogLevel.class);
+            showStacktraceSerializer = serializerFactory.getSerializerFor(ShowStacktrace.class);
+            consoleOutputSerializer = serializerFactory.getSerializerFor(ConsoleOutput.class);
+            warningModeSerializer = serializerFactory.getSerializerFor(WarningMode.class);
+        }
+
+        @Override
+        public void write(Encoder encoder, StartParameterInternal startParameter) throws Exception {
+            // Log configuration
+            logLevelSerializer.write(encoder, startParameter.getLogLevel());
+            showStacktraceSerializer.write(encoder, startParameter.getShowStacktrace());
+            consoleOutputSerializer.write(encoder, startParameter.getConsoleOutput());
+            warningModeSerializer.write(encoder, startParameter.getWarningMode());
+
+            // Parallel configuration
+            encoder.writeBoolean(startParameter.isParallelProjectExecutionEnabled());
+            encoder.writeSmallInt(startParameter.getMaxWorkerCount());
+
+            // Tasks
+            writeTaskRequests(encoder, startParameter.getTaskRequests());
+            stringSetSerializer.write(encoder, startParameter.getExcludedTaskNames());
+
+            // Layout
+            @SuppressWarnings("deprecation")
+            File customBuildFile = startParameter.getBuildFile();
+            nullableFileSerializer.write(encoder, customBuildFile);
+            nullableFileSerializer.write(encoder, startParameter.getProjectDir());
+            @SuppressWarnings("deprecation")
+            File customSettingsFile = startParameter.getSettingsFile();
+            nullableFileSerializer.write(encoder, customSettingsFile);
+            FILE_SERIALIZER.write(encoder, startParameter.getCurrentDir());
+            FILE_SERIALIZER.write(encoder, startParameter.getGradleUserHomeDir());
+            nullableFileSerializer.write(encoder, startParameter.getGradleHomeDir());
+            nullableFileSerializer.write(encoder, startParameter.getProjectCacheDir());
+            fileListSerializer.write(encoder, startParameter.getIncludedBuilds());
+
+            // Other stuff
+            NO_NULL_STRING_MAP_SERIALIZER.write(encoder, startParameter.getProjectProperties());
+            NO_NULL_STRING_MAP_SERIALIZER.write(encoder, startParameter.getSystemPropertiesArgs());
+            fileListSerializer.write(encoder, startParameter.getInitScripts());
+            stringListSerializer.write(encoder, startParameter.getLockedDependenciesToUpdate());
+
+            // Flags
+            encoder.writeBoolean(startParameter.isBuildProjectDependencies());
+            encoder.writeBoolean(startParameter.isDryRun());
+            encoder.writeBoolean(startParameter.isRerunTasks());
+            encoder.writeBoolean(startParameter.isProfile());
+            encoder.writeBoolean(startParameter.isContinueOnFailure());
+            encoder.writeBoolean(startParameter.isOffline());
+            encoder.writeBoolean(startParameter.isRefreshDependencies());
+            encoder.writeBoolean(startParameter.isBuildCacheEnabled());
+            encoder.writeBoolean(startParameter.isBuildCacheDebugLogging());
+            encoder.writeString(startParameter.getWatchFileSystemMode().name());
+            encoder.writeBoolean(startParameter.isWatchFileSystemDebugLogging());
+            encoder.writeBoolean(startParameter.isVfsVerboseLogging());
+            valueSerializer.write(encoder, startParameter.getConfigurationCache());
+            valueSerializer.write(encoder, startParameter.getIsolatedProjects());
+            encoder.writeString(startParameter.getConfigurationCacheProblems().name());
+            encoder.writeSmallInt(startParameter.getConfigurationCacheMaxProblems());
+            encoder.writeBoolean(startParameter.isConfigurationCacheRecreateCache());
+            encoder.writeBoolean(startParameter.isConfigurationCacheQuiet());
+            encoder.writeBoolean(startParameter.isConfigureOnDemand());
+            encoder.writeBoolean(startParameter.isContinuous());
+            encoder.writeBoolean(startParameter.isBuildScan());
+            encoder.writeBoolean(startParameter.isNoBuildScan());
+            encoder.writeBoolean(startParameter.isWriteDependencyLocks());
+            stringListSerializer.write(encoder, startParameter.getWriteDependencyVerifications());
+            encoder.writeString(startParameter.getDependencyVerificationMode().name());
+            encoder.writeBoolean(startParameter.isRefreshKeys());
+            encoder.writeBoolean(startParameter.isExportKeys());
+        }
+
+        private void writeTaskRequests(Encoder encoder, List<TaskExecutionRequest> taskRequests) throws Exception {
+            encoder.writeSmallInt(taskRequests.size());
+            for (TaskExecutionRequest taskRequest : taskRequests) {
+                if (!(taskRequest instanceof DefaultTaskExecutionRequest)) {
+                    // Only handle the command line for now
+                    throw new UnsupportedOperationException();
+                }
+                DefaultTaskExecutionRequest request = (DefaultTaskExecutionRequest) taskRequest;
+                encoder.writeNullableString(request.getProjectPath());
+                nullableFileSerializer.write(encoder, request.getRootDir());
+                stringListSerializer.write(encoder, request.getArgs());
+            }
+        }
+
+        @SuppressWarnings("deprecation") // StartParameter.setBuildFile and StartParameter.setSettingsFile
+        @Override
+        public StartParameterInternal read(Decoder decoder) throws Exception {
+            StartParameterInternal startParameter = new StartParameterInternal();
+
+            // Logging configuration
+            startParameter.setLogLevel(logLevelSerializer.read(decoder));
+            startParameter.setShowStacktrace(showStacktraceSerializer.read(decoder));
+            startParameter.setConsoleOutput(consoleOutputSerializer.read(decoder));
+            startParameter.setWarningMode(warningModeSerializer.read(decoder));
+
+            // Parallel configuration
+            startParameter.setParallelProjectExecutionEnabled(decoder.readBoolean());
+            startParameter.setMaxWorkerCount(decoder.readSmallInt());
+
+            // Tasks
+            startParameter.setTaskRequests(readTaskRequests(decoder));
+            startParameter.setExcludedTaskNames(stringSetSerializer.read(decoder));
+
+            // Layout
+            startParameter.setBuildFile(nullableFileSerializer.read(decoder));
+            startParameter.setProjectDir(nullableFileSerializer.read(decoder));
+            startParameter.setSettingsFile(nullableFileSerializer.read(decoder));
+            startParameter.setCurrentDir(FILE_SERIALIZER.read(decoder));
+            startParameter.setGradleUserHomeDir(FILE_SERIALIZER.read(decoder));
+            startParameter.setGradleHomeDir(nullableFileSerializer.read(decoder));
+            startParameter.setProjectCacheDir(nullableFileSerializer.read(decoder));
+            startParameter.setIncludedBuilds(fileListSerializer.read(decoder));
+
+            // Other stuff
+            startParameter.setProjectProperties(NO_NULL_STRING_MAP_SERIALIZER.read(decoder));
+            startParameter.setSystemPropertiesArgs(NO_NULL_STRING_MAP_SERIALIZER.read(decoder));
+            startParameter.setInitScripts(fileListSerializer.read(decoder));
+            startParameter.setLockedDependenciesToUpdate(stringListSerializer.read(decoder));
+
+            // Flags
+            startParameter.setBuildProjectDependencies(decoder.readBoolean());
+            startParameter.setDryRun(decoder.readBoolean());
+            startParameter.setRerunTasks(decoder.readBoolean());
+            startParameter.setProfile(decoder.readBoolean());
+            startParameter.setContinueOnFailure(decoder.readBoolean());
+            startParameter.setOffline(decoder.readBoolean());
+            startParameter.setRefreshDependencies(decoder.readBoolean());
+            startParameter.setBuildCacheEnabled(decoder.readBoolean());
+            startParameter.setBuildCacheDebugLogging(decoder.readBoolean());
+            startParameter.setWatchFileSystemMode(WatchMode.valueOf(decoder.readString()));
+            startParameter.setWatchFileSystemDebugLogging(decoder.readBoolean());
+            startParameter.setVfsVerboseLogging(decoder.readBoolean());
+            startParameter.setConfigurationCache(valueSerializer.read(decoder));
+            startParameter.setIsolatedProjects(valueSerializer.read(decoder));
+            startParameter.setConfigurationCacheProblems(ConfigurationCacheProblemsOption.Value.valueOf(decoder.readString()));
+            startParameter.setConfigurationCacheMaxProblems(decoder.readSmallInt());
+            startParameter.setConfigurationCacheRecreateCache(decoder.readBoolean());
+            startParameter.setConfigurationCacheQuiet(decoder.readBoolean());
+            startParameter.setConfigureOnDemand(decoder.readBoolean());
+            startParameter.setContinuous(decoder.readBoolean());
+            startParameter.setBuildScan(decoder.readBoolean());
+            startParameter.setNoBuildScan(decoder.readBoolean());
+            startParameter.setWriteDependencyLocks(decoder.readBoolean());
+            List<String> checksums = stringListSerializer.read(decoder);
+            if (!checksums.isEmpty()) {
+                startParameter.setWriteDependencyVerifications(checksums);
+            }
+            startParameter.setDependencyVerificationMode(DependencyVerificationMode.valueOf(decoder.readString()));
+            startParameter.setRefreshKeys(decoder.readBoolean());
+            startParameter.setExportKeys(decoder.readBoolean());
+
+            return startParameter;
+        }
+
+        private List<TaskExecutionRequest> readTaskRequests(Decoder decoder) throws Exception {
+            int requestCount = decoder.readSmallInt();
+            List<TaskExecutionRequest> taskExecutionRequests = new ArrayList<>(requestCount);
+            for (int i = 0; i < requestCount; i++) {
+                String projectPath = decoder.readNullableString();
+                File rootDir = nullableFileSerializer.read(decoder);
+                List<String> args = stringListSerializer.read(decoder);
+                taskExecutionRequests.add(new DefaultTaskExecutionRequest(args, projectPath, rootDir));
+            }
+            return taskExecutionRequests;
+        }
+    }
+
+    private static class ExecuteBuildActionSerializer implements Serializer<ExecuteBuildAction> {
+        private final Serializer<StartParameterInternal> startParameterSerializer = new StartParameterSerializer();
+
+        @Override
+        public void write(Encoder encoder, ExecuteBuildAction action) throws Exception {
+            StartParameterInternal startParameter = action.getStartParameter();
+            startParameterSerializer.write(encoder, startParameter);
+        }
+
+        @Override
+        public ExecuteBuildAction read(Decoder decoder) throws Exception {
+            StartParameterInternal startParameter = startParameterSerializer.read(decoder);
+            return new ExecuteBuildAction(startParameter);
+        }
+    }
+
+    private static class BuildModelActionSerializer implements Serializer<BuildModelAction> {
+        private final Serializer<StartParameterInternal> startParameterSerializer = new StartParameterSerializer();
+        private final Serializer<BuildEventSubscriptions> buildEventSubscriptionsSerializer = new BuildEventSubscriptionsSerializer();
+
+        @Override
+        public void write(Encoder encoder, BuildModelAction value) throws Exception {
+            startParameterSerializer.write(encoder, value.getStartParameter());
+            encoder.writeString(value.getModelName());
+            encoder.writeBoolean(value.isRunTasks());
+            buildEventSubscriptionsSerializer.write(encoder, value.getClientSubscriptions());
+        }
+
+        @Override
+        public BuildModelAction read(Decoder decoder) throws Exception {
+            StartParameterInternal startParameter = startParameterSerializer.read(decoder);
+            String modelName = decoder.readString();
+            boolean runTasks = decoder.readBoolean();
+            BuildEventSubscriptions buildEventSubscriptions = buildEventSubscriptionsSerializer.read(decoder);
+            return new BuildModelAction(startParameter, modelName, runTasks, buildEventSubscriptions);
+        }
+    }
+
+    private static class ClientProvidedBuildActionSerializer implements Serializer<ClientProvidedBuildAction> {
+        private final Serializer<StartParameterInternal> startParameterSerializer = new StartParameterSerializer();
+        private final Serializer<SerializedPayload> payloadSerializer = new SerializedPayloadSerializer();
+        private final Serializer<BuildEventSubscriptions> buildEventSubscriptionsSerializer = new BuildEventSubscriptionsSerializer();
+
+        @Override
+        public void write(Encoder encoder, ClientProvidedBuildAction value) throws Exception {
+            startParameterSerializer.write(encoder, value.getStartParameter());
+            payloadSerializer.write(encoder, value.getAction());
+            encoder.writeBoolean(value.isRunTasks());
+            buildEventSubscriptionsSerializer.write(encoder, value.getClientSubscriptions());
+        }
+
+        @Override
+        public ClientProvidedBuildAction read(Decoder decoder) throws Exception {
+            StartParameterInternal startParameter = startParameterSerializer.read(decoder);
+            SerializedPayload action = payloadSerializer.read(decoder);
+            boolean runTasks = decoder.readBoolean();
+            BuildEventSubscriptions buildEventSubscriptions = buildEventSubscriptionsSerializer.read(decoder);
+            return new ClientProvidedBuildAction(startParameter, action, runTasks, buildEventSubscriptions);
+        }
+    }
+
+    private static class ClientProvidedPhasedActionSerializer implements Serializer<ClientProvidedPhasedAction> {
+        private final Serializer<StartParameterInternal> startParameterSerializer = new StartParameterSerializer();
+        private final Serializer<SerializedPayload> payloadSerializer = new SerializedPayloadSerializer();
+        private final Serializer<BuildEventSubscriptions> buildEventSubscriptionsSerializer = new BuildEventSubscriptionsSerializer();
+
+        @Override
+        public void write(Encoder encoder, ClientProvidedPhasedAction value) throws Exception {
+            startParameterSerializer.write(encoder, value.getStartParameter());
+            payloadSerializer.write(encoder, value.getPhasedAction());
+            encoder.writeBoolean(value.isRunTasks());
+            buildEventSubscriptionsSerializer.write(encoder, value.getClientSubscriptions());
+        }
+
+        @Override
+        public ClientProvidedPhasedAction read(Decoder decoder) throws Exception {
+            StartParameterInternal startParameter = startParameterSerializer.read(decoder);
+            SerializedPayload action = payloadSerializer.read(decoder);
+            boolean runTasks = decoder.readBoolean();
+            BuildEventSubscriptions buildEventSubscriptions = buildEventSubscriptionsSerializer.read(decoder);
+            return new ClientProvidedPhasedAction(startParameter, action, runTasks, buildEventSubscriptions);
+        }
+    }
+
+    private static class TestExecutionRequestPayload implements Serializable {
+        final Set<InternalTestDescriptor> testDescriptors;
+        final Set<String> classNames;
+        final Set<InternalJvmTestRequest> internalJvmTestRequests;
+        final InternalDebugOptions debugOptions;
+        final Map<String, List<InternalJvmTestRequest>> taskAndTests;
+
+        public TestExecutionRequestPayload(Set<InternalTestDescriptor> testDescriptors, Set<String> classNames, Set<InternalJvmTestRequest> internalJvmTestRequests, InternalDebugOptions debugOptions, Map<String, List<InternalJvmTestRequest>> taskAndTests) {
+            this.testDescriptors = testDescriptors;
+            this.classNames = classNames;
+            this.internalJvmTestRequests = internalJvmTestRequests;
+            this.debugOptions = debugOptions;
+            this.taskAndTests = taskAndTests;
+        }
+    }
+
+    private static class TestExecutionRequestActionSerializer implements Serializer<TestExecutionRequestAction> {
+        private final Serializer<StartParameterInternal> startParameterSerializer = new StartParameterSerializer();
+        private final Serializer<BuildEventSubscriptions> buildEventSubscriptionsSerializer = new BuildEventSubscriptionsSerializer();
+        private final Serializer<TestExecutionRequestPayload> payloadSerializer = new DefaultSerializer<>();
+
+        @Override
+        public void write(Encoder encoder, TestExecutionRequestAction value) throws Exception {
+            startParameterSerializer.write(encoder, value.getStartParameter());
+            buildEventSubscriptionsSerializer.write(encoder, value.getClientSubscriptions());
+            payloadSerializer.write(encoder, new TestExecutionRequestPayload(
+                value.getTestExecutionDescriptors(),
+                value.getTestClassNames(),
+                value.getInternalJvmTestRequests(),
+                value.getDebugOptions(),
+                value.getTaskAndTests()
+            ));
+        }
+
+        @Override
+        public TestExecutionRequestAction read(Decoder decoder) throws Exception {
+            StartParameterInternal startParameter = startParameterSerializer.read(decoder);
+            BuildEventSubscriptions buildEventSubscriptions = buildEventSubscriptionsSerializer.read(decoder);
+            TestExecutionRequestPayload payload = payloadSerializer.read(decoder);
+            return new TestExecutionRequestAction(buildEventSubscriptions, startParameter, payload.testDescriptors, payload.classNames, payload.internalJvmTestRequests, payload.debugOptions, payload.taskAndTests);
+        }
+    }
+
+    private static class BuildEventSubscriptionsSerializer implements Serializer<BuildEventSubscriptions> {
+        private final Serializer<Set<OperationType>> setSerializer;
+
+        public BuildEventSubscriptionsSerializer() {
+            BaseSerializerFactory serializerFactory = new BaseSerializerFactory();
+            this.setSerializer = new SetSerializer<>(serializerFactory.getSerializerFor(OperationType.class));
+        }
+
+        @Override
+        public void write(Encoder encoder, BuildEventSubscriptions value) throws Exception {
+            setSerializer.write(encoder, value.getOperationTypes());
+        }
+
+        @Override
+        public BuildEventSubscriptions read(Decoder decoder) throws Exception {
+            return new BuildEventSubscriptions(setSerializer.read(decoder));
+        }
+    }
+
+    private static class ValueSerializer implements Serializer<BuildOption.Value<Boolean>> {
+        private static final byte EXPLICIT_TRUE = (byte) 1;
+        private static final byte EXPLICIT_FALSE = (byte) 2;
+        public static final byte IMPLICIT_TRUE = (byte) 3;
+        public static final byte IMPLICIT_FALSE = (byte) 4;
+
+        @Override
+        public BuildOption.Value<Boolean> read(Decoder decoder) throws Exception {
+            switch (decoder.readByte()) {
+                case EXPLICIT_TRUE:
+                    return BuildOption.Value.value(true);
+                case EXPLICIT_FALSE:
+                    return BuildOption.Value.value(false);
+                case IMPLICIT_TRUE:
+                    return BuildOption.Value.defaultValue(true);
+                case IMPLICIT_FALSE:
+                    return BuildOption.Value.defaultValue(false);
+                default:
+                    throw new IllegalStateException();
+            }
+        }
+
+        @Override
+        public void write(Encoder encoder, BuildOption.Value<Boolean> value) throws Exception {
+            if (value.isExplicit() && value.get()) {
+                encoder.writeByte(EXPLICIT_TRUE);
+            } else if (value.isExplicit()) {
+                encoder.writeByte(EXPLICIT_FALSE);
+            } else if (value.get()) {
+                encoder.writeByte(IMPLICIT_TRUE);
+            } else {
+                encoder.writeByte(IMPLICIT_FALSE);
+            }
+        }
+    }
+
+    private static class NullableFileSerializer implements Serializer<File> {
+        @Override
+        public void write(Encoder encoder, File value) throws Exception {
+            if (value == null) {
+                encoder.writeBoolean(false);
+            } else {
+                encoder.writeBoolean(true);
+                encoder.writeString(value.getPath());
+            }
+        }
+
+        @Override
+        public File read(Decoder decoder) throws Exception {
+            if (decoder.readBoolean()) {
+                return new File(decoder.readString());
+            }
+            return null;
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/BuildModelAction.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/BuildModelAction.java
similarity index 93%
rename from subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/BuildModelAction.java
rename to subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/BuildModelAction.java
index baecc2d..2ed44ca 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/BuildModelAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/BuildModelAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2011 the original author or authors.
+ * Copyright 2021 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.gradle.tooling.internal.provider;
+package org.gradle.tooling.internal.provider.action;
 
 import org.gradle.api.internal.StartParameterInternal;
 import org.gradle.internal.build.event.BuildEventSubscriptions;
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClientProvidedBuildAction.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/ClientProvidedBuildAction.java
similarity index 93%
rename from subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClientProvidedBuildAction.java
rename to subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/ClientProvidedBuildAction.java
index 4f3730c..8ec9750 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClientProvidedBuildAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/ClientProvidedBuildAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 the original author or authors.
+ * Copyright 2021 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package org.gradle.tooling.internal.provider;
+package org.gradle.tooling.internal.provider.action;
 
 import org.gradle.api.internal.StartParameterInternal;
-import org.gradle.tooling.internal.provider.serialization.SerializedPayload;
 import org.gradle.internal.build.event.BuildEventSubscriptions;
+import org.gradle.tooling.internal.provider.serialization.SerializedPayload;
 
 public class ClientProvidedBuildAction extends SubscribableBuildAction {
     private final StartParameterInternal startParameter;
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClientProvidedPhasedAction.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/ClientProvidedPhasedAction.java
similarity index 93%
rename from subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClientProvidedPhasedAction.java
rename to subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/ClientProvidedPhasedAction.java
index ac30180..e82108a 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClientProvidedPhasedAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/ClientProvidedPhasedAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 the original author or authors.
+ * Copyright 2021 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package org.gradle.tooling.internal.provider;
+package org.gradle.tooling.internal.provider.action;
 
 import org.gradle.api.internal.StartParameterInternal;
-import org.gradle.tooling.internal.provider.serialization.SerializedPayload;
 import org.gradle.internal.build.event.BuildEventSubscriptions;
+import org.gradle.tooling.internal.provider.serialization.SerializedPayload;
 
 public class ClientProvidedPhasedAction extends SubscribableBuildAction {
     private final StartParameterInternal startParameter;
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/action/ExecuteBuildAction.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/ExecuteBuildAction.java
similarity index 91%
rename from subprojects/launcher/src/main/java/org/gradle/launcher/cli/action/ExecuteBuildAction.java
rename to subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/ExecuteBuildAction.java
index c28e506..f962b57 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/action/ExecuteBuildAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/ExecuteBuildAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 the original author or authors.
+ * Copyright 2021 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.gradle.launcher.cli.action;
+package org.gradle.tooling.internal.provider.action;
 
 import org.gradle.api.internal.StartParameterInternal;
 import org.gradle.internal.invocation.BuildAction;
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/SubscribableBuildAction.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/SubscribableBuildAction.java
similarity index 87%
rename from subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/SubscribableBuildAction.java
rename to subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/SubscribableBuildAction.java
index 82af652..869f08d 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/SubscribableBuildAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/SubscribableBuildAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 the original author or authors.
+ * Copyright 2021 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-package org.gradle.tooling.internal.provider;
+package org.gradle.tooling.internal.provider.action;
 
 import org.gradle.internal.build.event.BuildEventSubscriptions;
 import org.gradle.internal.invocation.BuildAction;
 
-import java.io.Serializable;
-
-public abstract class SubscribableBuildAction implements BuildAction, Serializable {
+public abstract class SubscribableBuildAction implements BuildAction {
     private final BuildEventSubscriptions clientSubscriptions;
 
     public SubscribableBuildAction(BuildEventSubscriptions clientSubscriptions) {
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/TestExecutionRequestAction.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/TestExecutionRequestAction.java
similarity index 89%
rename from subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/TestExecutionRequestAction.java
rename to subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/TestExecutionRequestAction.java
index 82df886..deaa188 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/TestExecutionRequestAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/action/TestExecutionRequestAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 the original author or authors.
+ * Copyright 2021 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.gradle.tooling.internal.provider;
+package org.gradle.tooling.internal.provider.action;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -43,7 +43,7 @@
     private final InternalDebugOptions debugOptions;
     private final Map<String, List<InternalJvmTestRequest>> taskAndTests;
 
-    private TestExecutionRequestAction(BuildEventSubscriptions clientSubscriptions, StartParameterInternal startParameter, Set<InternalTestDescriptor> testDescriptors, Set<String> providerClassNames, Set<InternalJvmTestRequest> internalJvmTestRequests, InternalDebugOptions debugOptions, Map<String, List<InternalJvmTestRequest>> taskAndTests) {
+    public TestExecutionRequestAction(BuildEventSubscriptions clientSubscriptions, StartParameterInternal startParameter, Set<InternalTestDescriptor> testDescriptors, Set<String> providerClassNames, Set<InternalJvmTestRequest> internalJvmTestRequests, InternalDebugOptions debugOptions, Map<String, List<InternalJvmTestRequest>> taskAndTests) {
         super(clientSubscriptions);
         this.startParameter = startParameter;
         this.testDescriptors = testDescriptors;
@@ -117,15 +117,15 @@
         return false;
     }
 
-    public Collection<String> getTestClassNames() {
+    public Set<String> getTestClassNames() {
         return classNames;
     }
 
-    public Collection<InternalJvmTestRequest> getInternalJvmTestRequests() {
+    public Set<InternalJvmTestRequest> getInternalJvmTestRequests() {
         return internalJvmTestRequests;
     }
 
-    public Collection<InternalTestDescriptor> getTestExecutionDescriptors() {
+    public Set<InternalTestDescriptor> getTestExecutionDescriptors() {
         return testDescriptors;
     }
 
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/BuildActionSerializerTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/BuildActionSerializerTest.groovy
deleted file mode 100644
index a0032a1..0000000
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/BuildActionSerializerTest.groovy
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2018 the original author or authors.
- *
- * Licensed 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.gradle.launcher.cli
-
-import org.gradle.api.internal.StartParameterInternal
-import org.gradle.internal.build.event.BuildEventSubscriptions
-import org.gradle.internal.serialize.SerializerSpec
-import org.gradle.launcher.cli.action.BuildActionSerializer
-import org.gradle.launcher.cli.action.ExecuteBuildAction
-import org.gradle.tooling.events.OperationType
-import org.gradle.tooling.events.test.internal.DefaultDebugOptions
-import org.gradle.tooling.internal.provider.BuildModelAction
-import org.gradle.tooling.internal.provider.ClientProvidedBuildAction
-import org.gradle.tooling.internal.provider.TestExecutionRequestAction
-import org.gradle.tooling.internal.provider.serialization.SerializedPayload
-import spock.lang.Unroll
-
-import java.beans.Introspector
-
-@Unroll
-class BuildActionSerializerTest extends SerializerSpec {
-    def "serializes ExecuteBuildAction with all defaults"() {
-        def action = new ExecuteBuildAction(new StartParameterInternal())
-
-        expect:
-        def result = serialize(action, BuildActionSerializer.create())
-        result instanceof ExecuteBuildAction
-    }
-
-    def "serializes ExecuteBuildAction with non-defaults"() {
-        def startParameter = new StartParameterInternal()
-        startParameter.taskNames = ['a', 'b']
-        def action = new ExecuteBuildAction(startParameter)
-
-        expect:
-        def result = serialize(action, BuildActionSerializer.create())
-        result instanceof ExecuteBuildAction
-        result.startParameter.taskNames == ['a', 'b']
-    }
-
-    def "serializes #buildOptionName boolean build option"() {
-        def startParameter = new StartParameterInternal()
-        boolean expectedValue = !startParameter."${buildOptionName}"
-        startParameter."${buildOptionName}" = expectedValue
-        def action = new ExecuteBuildAction(startParameter)
-
-        expect:
-        def result = serialize(action, BuildActionSerializer.create())
-        result instanceof ExecuteBuildAction
-        result.startParameter."${buildOptionName}" == expectedValue
-
-        where:
-        // Check all mutable boolean properties (must manually check for setters as many of them return StartParameter)
-        buildOptionName << Introspector.getBeanInfo(StartParameterInternal).propertyDescriptors
-            .findAll { it.propertyType == boolean }
-            .findAll { StartParameterInternal.methods*.name.contains("set" + it.name.capitalize()) }
-            .collect { it.name }
-    }
-
-    def "serializes other actions #action.class"() {
-        expect:
-        def result = serialize(action, BuildActionSerializer.create())
-        result.class == action.class
-
-        where:
-        action << [
-            new ClientProvidedBuildAction(new StartParameterInternal(), new SerializedPayload(null, []), true, new BuildEventSubscriptions(EnumSet.allOf(OperationType))),
-            new TestExecutionRequestAction(new BuildEventSubscriptions(EnumSet.allOf(OperationType)), new StartParameterInternal(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), new DefaultDebugOptions(), Collections.emptyMap()),
-            new BuildModelAction(new StartParameterInternal(), "model", false, new BuildEventSubscriptions(EnumSet.allOf(OperationType)))
-        ]
-    }
-}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/RunBuildActionTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/RunBuildActionTest.groovy
index 722e5fa..1dfad76 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/RunBuildActionTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/RunBuildActionTest.groovy
@@ -24,7 +24,7 @@
 import org.gradle.internal.concurrent.Stoppable
 import org.gradle.internal.nativeintegration.console.ConsoleDetector
 import org.gradle.internal.service.ServiceRegistry
-import org.gradle.launcher.cli.action.ExecuteBuildAction
+import org.gradle.tooling.internal.provider.action.ExecuteBuildAction
 import org.gradle.launcher.exec.BuildActionExecuter
 import org.gradle.launcher.exec.BuildActionParameters
 import org.gradle.launcher.exec.BuildActionResult
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/protocol/DaemonMessageSerializerTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/protocol/DaemonMessageSerializerTest.groovy
index 4e3c70c..f81b0ae 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/protocol/DaemonMessageSerializerTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/protocol/DaemonMessageSerializerTest.groovy
@@ -28,8 +28,8 @@
 import org.gradle.internal.serialize.PlaceholderException
 import org.gradle.internal.serialize.Serializer
 import org.gradle.internal.serialize.SerializerSpec
-import org.gradle.launcher.cli.action.BuildActionSerializer
-import org.gradle.launcher.cli.action.ExecuteBuildAction
+import org.gradle.tooling.internal.provider.action.BuildActionSerializer
+import org.gradle.tooling.internal.provider.action.ExecuteBuildAction
 import org.gradle.launcher.daemon.diagnostics.DaemonDiagnostics
 import org.gradle.launcher.exec.BuildActionResult
 import org.gradle.launcher.exec.DefaultBuildActionParameters
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/SubscribableBuildActionExecuterSpec.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/SubscribableBuildActionExecuterSpec.groovy
index 8c4d3ba..abb7a32 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/SubscribableBuildActionExecuterSpec.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/SubscribableBuildActionExecuterSpec.groovy
@@ -25,6 +25,7 @@
 import org.gradle.internal.session.BuildSessionContext
 import org.gradle.internal.session.BuildSessionActionExecutor
 import org.gradle.tooling.events.OperationType
+import org.gradle.tooling.internal.provider.action.SubscribableBuildAction
 import spock.lang.Specification
 
 class SubscribableBuildActionExecuterSpec extends Specification {
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/TestExecutionRequestActionTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/TestExecutionRequestActionTest.groovy
index 3be00be..965e450 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/TestExecutionRequestActionTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/TestExecutionRequestActionTest.groovy
@@ -18,6 +18,7 @@
 
 import org.gradle.api.internal.StartParameterInternal
 import org.gradle.internal.build.event.BuildEventSubscriptions
+import org.gradle.tooling.internal.provider.action.TestExecutionRequestAction
 import org.gradle.tooling.internal.provider.test.ProviderInternalTestExecutionRequest
 import spock.lang.Specification
 
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/action/BuildActionSerializerTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/action/BuildActionSerializerTest.groovy
new file mode 100644
index 0000000..203179d
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/action/BuildActionSerializerTest.groovy
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed 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.gradle.tooling.internal.provider.action
+
+import org.gradle.api.internal.StartParameterInternal
+import org.gradle.internal.build.event.BuildEventSubscriptions
+import org.gradle.internal.serialize.SerializerSpec
+import org.gradle.tooling.events.OperationType
+import org.gradle.tooling.events.test.internal.DefaultDebugOptions
+import org.gradle.tooling.internal.provider.serialization.SerializedPayload
+import spock.lang.Unroll
+
+import java.beans.Introspector
+
+@Unroll
+class BuildActionSerializerTest extends SerializerSpec {
+    def "serializes ExecuteBuildAction with all defaults"() {
+        def action = new ExecuteBuildAction(new StartParameterInternal())
+
+        expect:
+        def result = serialize(action, BuildActionSerializer.create())
+        result instanceof ExecuteBuildAction
+    }
+
+    def "serializes ExecuteBuildAction with non-defaults"() {
+        def startParameter = new StartParameterInternal()
+        startParameter.taskNames = ['a', 'b']
+        def action = new ExecuteBuildAction(startParameter)
+
+        expect:
+        def result = serialize(action, BuildActionSerializer.create())
+        result instanceof ExecuteBuildAction
+        result.startParameter.taskNames == ['a', 'b']
+    }
+
+    def "serializes #buildOptionName boolean build option"() {
+        def startParameter = new StartParameterInternal()
+        boolean expectedValue = !startParameter."${buildOptionName}"
+        startParameter."${buildOptionName}" = expectedValue
+        def action = new ExecuteBuildAction(startParameter)
+
+        expect:
+        def result = serialize(action, BuildActionSerializer.create())
+        result instanceof ExecuteBuildAction
+        result.startParameter."${buildOptionName}" == expectedValue
+
+        where:
+        // Check all mutable boolean properties (must manually check for setters as many of them return StartParameter)
+        buildOptionName << Introspector.getBeanInfo(StartParameterInternal).propertyDescriptors
+            .findAll { it.propertyType == boolean }
+            .findAll { p -> StartParameterInternal.methods.find { m -> m.name == "set" + p.name.capitalize() && m.parameterCount == 1 && m.parameterTypes[0] == Boolean.TYPE } }
+            .collect { it.name }
+    }
+
+    def "serializes BuildModelAction"() {
+        def startParameter = new StartParameterInternal()
+        startParameter.taskNames = ['a', 'b']
+        def action = new BuildModelAction(startParameter, "model", true, new BuildEventSubscriptions([OperationType.TASK] as Set))
+
+        expect:
+        def result = serialize(action, BuildActionSerializer.create())
+        result instanceof BuildModelAction
+        result.startParameter.taskNames == ['a', 'b']
+        result.modelName == "model"
+        result.runTasks
+        result.clientSubscriptions.operationTypes == [OperationType.TASK] as Set
+    }
+
+    def "serializes ClientProvidedBuildAction"() {
+        def startParameter = new StartParameterInternal()
+        startParameter.taskNames = ['a', 'b']
+        def action = new ClientProvidedBuildAction(startParameter, new SerializedPayload("12", []), true, new BuildEventSubscriptions([OperationType.TASK] as Set))
+
+        expect:
+        def result = serialize(action, BuildActionSerializer.create())
+        result instanceof ClientProvidedBuildAction
+        result.startParameter.taskNames == ['a', 'b']
+        result.action.header == "12"
+        result.runTasks
+        result.clientSubscriptions.operationTypes == [OperationType.TASK] as Set
+    }
+
+    def "serializes ClientProvidedPhasedAction"() {
+        def startParameter = new StartParameterInternal()
+        startParameter.taskNames = ['a', 'b']
+        def action = new ClientProvidedPhasedAction(startParameter, new SerializedPayload("12", []), true, new BuildEventSubscriptions([OperationType.TASK] as Set))
+
+        expect:
+        def result = serialize(action, BuildActionSerializer.create())
+        result instanceof ClientProvidedPhasedAction
+        result.startParameter.taskNames == ['a', 'b']
+        result.phasedAction.header == "12"
+        result.runTasks
+        result.clientSubscriptions.operationTypes == [OperationType.TASK] as Set
+    }
+
+    def "serializes TestExecutionRequestAction"() {
+        def startParameter = new StartParameterInternal()
+        def action = new TestExecutionRequestAction(new BuildEventSubscriptions([OperationType.TASK] as Set), startParameter, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), new DefaultDebugOptions(), Collections.emptyMap())
+
+        expect:
+        def result = serialize(action, BuildActionSerializer.create())
+        result instanceof TestExecutionRequestAction
+        result.clientSubscriptions.operationTypes == [OperationType.TASK] as Set
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativePlatformSamplesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativePlatformSamplesIntegrationTest.groovy
index dd01aed..526fdcb 100644
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativePlatformSamplesIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativePlatformSamplesIntegrationTest.groovy
@@ -15,8 +15,8 @@
  */
 package org.gradle.nativeplatform
 
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
@@ -24,7 +24,6 @@
 import org.gradle.util.Requires
 import org.gradle.util.TestPrecondition
 import org.junit.Rule
-import spock.lang.Ignore
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.SUPPORTS_32
@@ -32,7 +31,6 @@
 import static org.junit.Assume.assumeTrue
 
 @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-@Ignore("https://github.com/gradle/gradle-private/issues/3369")
 class NativePlatformSamplesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     @Rule public final Sample cppLib = sample(testDirectoryProvider, 'cpp-lib')
     @Rule public final Sample cppExe = sample(testDirectoryProvider, 'cpp-exe')
@@ -256,6 +254,10 @@
     }
 
     @ToBeFixedForConfigurationCache
+    @RequiresInstalledToolChain(GCC_COMPATIBLE) // latest clang seems to have issues:
+    // /usr/bin/ld: /home/tcagent1/agent/work/e67123fb5b9af0ac/subprojects/platform-native/build/tmp/test files/NativePlatf.Test/89jnk/sourceset-variant/build/objs/main/mainExecutablePlatformLinux/3aor34f2b62iejk2eq3fn5ikr/platform-linux.o:(.data+0x0): multiple definition of `platform_name';
+    // /home/tcagent1/agent/work/e67123fb5b9af0ac/subprojects/platform-native/build/tmp/test files/NativePlatf.Test/89jnk/sourceset-variant/build/objs/main/mainC/dey3oyi6y0a9luwot945rff8j/main.o:(.bss+0x0): first defined here
+    //clang: error: linker command failed with exit code 1 (use -v to see invocation)
     def sourcesetvariant() {
         given:
         sample sourcesetVariant
diff --git a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/GpgCmdFixture.groovy b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/GpgCmdFixture.groovy
index 4b39c0c..33f2855 100644
--- a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/GpgCmdFixture.groovy
+++ b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/GpgCmdFixture.groovy
@@ -22,33 +22,28 @@
 import java.nio.file.Files
 import java.nio.file.Path
 import java.nio.file.Paths
+import java.util.regex.Pattern
 
 class GpgCmdFixture {
     private static final Random RANDOM = new Random()
     private static final int ALL_DIGITS_AND_LETTERS_RADIX = 36
     private static final int MAX_RANDOM_PART_VALUE = Integer.valueOf("zzzzz", ALL_DIGITS_AND_LETTERS_RADIX)
+    private static final Pattern GPG_VERSION_REGEX = Pattern.compile(/(?s).*gpg \(GnuPG\) (\d).\d+.\d+.*/)
 
     static String createRandomPrefix() {
         return Integer.toString(RANDOM.nextInt(MAX_RANDOM_PART_VALUE), ALL_DIGITS_AND_LETTERS_RADIX)
     }
 
-    static String getAvailableGpg() {
-        if (tryRun('gpg2')) {
-            return 'gpg2'
-        } else if (tryRun('gpg')) {
-            return 'gpg'
-        } else {
-            return null
-        }
+    static GpgCmdAndVersion getAvailableGpg() {
+        return tryRun("gpg2") ?: tryRun("gpg")
     }
 
-    static boolean tryRun(String cmd) {
+    static GpgCmdAndVersion tryRun(String cmd) {
         try {
-            "${cmd} --version".execute()
-            return true
-        }
-        catch (IOException e) {
-            return false
+            String output = "${cmd} --version".execute().text
+            return new GpgCmdAndVersion(executable: cmd, version: (output =~ GPG_VERSION_REGEX)[0][1].toInteger())
+        } catch (Exception ignored) {
+            return null
         }
     }
 
@@ -56,10 +51,10 @@
         def gpgHomeSymlink = prepareGnupgHomeSymlink(buildDir.file('gnupg-home'))
         Properties properties = new Properties()
         properties.load(buildDir.file('gradle.properties').newInputStream())
-        String client = GpgCmdFixture.getAvailableGpg()
+        GpgCmdAndVersion client = getAvailableGpg()
         assert client
-        properties.put('signing.gnupg.executable', client)
-        properties.put('signing.gnupg.useLegacyGpg', (client == 'gpg').toString())
+        properties.put('signing.gnupg.executable', client.executable)
+        properties.put('signing.gnupg.useLegacyGpg', (client.version == 1).toString())
         properties.put('signing.gnupg.homeDir', gpgHomeSymlink.toAbsolutePath().toString())
         properties.remove('signing.gnupg.optionsFile')
         properties.store(buildDir.file('gradle.properties').newOutputStream(), '')
@@ -78,3 +73,10 @@
         return Files.createSymbolicLink(tmpLink, gpgHomeInTest.toPath())
     }
 }
+
+class GpgCmdAndVersion {
+    // gpg or gpg2
+    String executable
+    // 1 or 2
+    int version
+}
diff --git a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningConfigurationsWithGpgCmdIntegrationSpec.groovy b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningConfigurationsWithGpgCmdIntegrationSpec.groovy
index bf543ea..67ec109 100644
--- a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningConfigurationsWithGpgCmdIntegrationSpec.groovy
+++ b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningConfigurationsWithGpgCmdIntegrationSpec.groovy
@@ -18,10 +18,8 @@
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.plugins.signing.signatory.internal.gnupg.GnupgSignatoryProvider
 import org.gradle.util.Requires
-import spock.lang.Ignore
 
 @Requires(adhoc = { GpgCmdFixture.getAvailableGpg() != null })
-@Ignore('https://github.com/gradle/gradle-private/issues/3370')
 class SigningConfigurationsWithGpgCmdIntegrationSpec extends SigningConfigurationsIntegrationSpec {
     SignMethod getSignMethod() {
         return SignMethod.GPG_CMD
diff --git a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningSamplesSpec.groovy b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningSamplesSpec.groovy
index 1ed5b29..221b252 100644
--- a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningSamplesSpec.groovy
+++ b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningSamplesSpec.groovy
@@ -23,10 +23,8 @@
 import org.gradle.test.fixtures.maven.MavenFileRepository
 import org.gradle.util.Requires
 import org.junit.Rule
-import spock.lang.Ignore
 import spock.lang.Unroll
 
-@Ignore('https://github.com/gradle/gradle-private/issues/3370')
 class SigningSamplesSpec extends AbstractSampleIntegrationTest {
     @Rule
     public final Sample sample = new Sample(testDirectoryProvider)
diff --git a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningTasksIntegrationSpec.groovy b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningTasksIntegrationSpec.groovy
index a6c967e..b82ebfd 100644
--- a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningTasksIntegrationSpec.groovy
+++ b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningTasksIntegrationSpec.groovy
@@ -20,12 +20,10 @@
 import org.gradle.plugins.signing.signatory.pgp.PgpSignatoryProvider
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.util.Requires
-import spock.lang.Ignore
 
 import static org.gradle.plugins.signing.SigningIntegrationSpec.SignMethod.GPG_CMD
 import static org.gradle.plugins.signing.SigningIntegrationSpec.SignMethod.OPEN_GPG
 
-@Ignore('https://github.com/gradle/gradle-private/issues/3370')
 class SigningTasksIntegrationSpec extends SigningIntegrationSpec {
 
     @ToBeFixedForConfigurationCache
diff --git a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningTasksWithGpgCmdIntegrationSpec.groovy b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningTasksWithGpgCmdIntegrationSpec.groovy
index a2b5235..050fba0 100644
--- a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningTasksWithGpgCmdIntegrationSpec.groovy
+++ b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningTasksWithGpgCmdIntegrationSpec.groovy
@@ -16,10 +16,8 @@
 package org.gradle.plugins.signing
 
 import org.gradle.util.Requires
-import spock.lang.Ignore
 
 @Requires(adhoc = { GpgCmdFixture.getAvailableGpg() != null })
-@Ignore('https://github.com/gradle/gradle-private/issues/3370')
 class SigningTasksWithGpgCmdIntegrationSpec extends SigningTasksIntegrationSpec {
     SignMethod getSignMethod() {
         return SignMethod.GPG_CMD
diff --git a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/BuildModelActionRunner.java b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/BuildModelActionRunner.java
index 87944fb..b3f78f4 100644
--- a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/BuildModelActionRunner.java
+++ b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/BuildModelActionRunner.java
@@ -27,7 +27,7 @@
 import org.gradle.internal.buildtree.BuildActionRunner;
 import org.gradle.internal.buildtree.BuildTreeLifecycleController;
 import org.gradle.tooling.internal.protocol.InternalUnsupportedModelException;
-import org.gradle.tooling.internal.provider.BuildModelAction;
+import org.gradle.tooling.internal.provider.action.BuildModelAction;
 import org.gradle.tooling.provider.model.UnknownModelException;
 import org.gradle.tooling.provider.model.internal.ToolingModelBuilderLookup;
 
diff --git a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunner.java b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunner.java
index a1b0435..0a4c1e4 100644
--- a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunner.java
+++ b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunner.java
@@ -21,7 +21,7 @@
 import org.gradle.internal.buildtree.BuildActionRunner;
 import org.gradle.internal.buildtree.BuildTreeLifecycleController;
 import org.gradle.tooling.internal.protocol.PhasedActionResult;
-import org.gradle.tooling.internal.provider.ClientProvidedBuildAction;
+import org.gradle.tooling.internal.provider.action.ClientProvidedBuildAction;
 import org.gradle.tooling.internal.provider.serialization.PayloadSerializer;
 
 public class ClientProvidedBuildActionRunner extends AbstractClientProvidedBuildActionRunner implements BuildActionRunner {
diff --git a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientProvidedPhasedActionRunner.java b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientProvidedPhasedActionRunner.java
index 1015df8..96ae2e3 100644
--- a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientProvidedPhasedActionRunner.java
+++ b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientProvidedPhasedActionRunner.java
@@ -23,7 +23,7 @@
 import org.gradle.internal.buildtree.BuildTreeLifecycleController;
 import org.gradle.tooling.internal.protocol.InternalPhasedAction;
 import org.gradle.tooling.internal.protocol.PhasedActionResult;
-import org.gradle.tooling.internal.provider.ClientProvidedPhasedAction;
+import org.gradle.tooling.internal.provider.action.ClientProvidedPhasedAction;
 import org.gradle.tooling.internal.provider.PhasedBuildActionResult;
 import org.gradle.tooling.internal.provider.serialization.PayloadSerializer;
 import org.gradle.tooling.internal.provider.serialization.SerializedPayload;
diff --git a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TestExecutionBuildConfigurationAction.java b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TestExecutionBuildConfigurationAction.java
index 491e62e..0025af8 100644
--- a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TestExecutionBuildConfigurationAction.java
+++ b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TestExecutionBuildConfigurationAction.java
@@ -36,7 +36,7 @@
 import org.gradle.tooling.internal.protocol.events.InternalTestDescriptor;
 import org.gradle.tooling.internal.protocol.test.InternalDebugOptions;
 import org.gradle.tooling.internal.protocol.test.InternalJvmTestRequest;
-import org.gradle.tooling.internal.provider.TestExecutionRequestAction;
+import org.gradle.tooling.internal.provider.action.TestExecutionRequestAction;
 import org.gradle.util.internal.CollectionUtils;
 
 import java.util.ArrayList;
@@ -64,7 +64,7 @@
         final GradleInternal gradleInternal = context.getGradle();
         allTestTasksToRun.addAll(configureBuildForTestDescriptors(testExecutionRequest));
         allTestTasksToRun.addAll(configureBuildForInternalJvmTestRequest(gradleInternal, testExecutionRequest));
-        allTestTasksToRun.addAll(configureBuildForTestTasks(gradleInternal, testExecutionRequest));
+        allTestTasksToRun.addAll(configureBuildForTestTasks(testExecutionRequest));
         configureTestTasks(allTestTasksToRun);
         gradle.getTaskGraph().addEntryTasks(allTestTasksToRun);
     }
@@ -107,7 +107,7 @@
         return testTasksToRun;
     }
 
-    private List<Test> configureBuildForTestTasks(GradleInternal gradle, TestExecutionRequestAction testExecutionRequest) {
+    private List<Test> configureBuildForTestTasks(TestExecutionRequestAction testExecutionRequest) {
         final Collection<InternalTestDescriptor> testDescriptors = testExecutionRequest.getTestExecutionDescriptors();
 
         final List<String> testTaskPaths = CollectionUtils.collect(testDescriptors, new Transformer<String, InternalTestDescriptor>() {
diff --git a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TestExecutionRequestActionRunner.java b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TestExecutionRequestActionRunner.java
index 005f9a7..13600a6 100644
--- a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TestExecutionRequestActionRunner.java
+++ b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TestExecutionRequestActionRunner.java
@@ -24,7 +24,7 @@
 import org.gradle.internal.operations.BuildOperationAncestryTracker;
 import org.gradle.internal.operations.BuildOperationListenerManager;
 import org.gradle.tooling.internal.protocol.test.InternalTestExecutionException;
-import org.gradle.tooling.internal.provider.TestExecutionRequestAction;
+import org.gradle.tooling.internal.provider.action.TestExecutionRequestAction;
 
 import java.util.Collections;
 
diff --git a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TestExecutionResultEvaluator.java b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TestExecutionResultEvaluator.java
index f9d4cac..d8dc5cb 100644
--- a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TestExecutionResultEvaluator.java
+++ b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TestExecutionResultEvaluator.java
@@ -34,7 +34,7 @@
 import org.gradle.internal.operations.OperationStartEvent;
 import org.gradle.tooling.internal.protocol.events.InternalTestDescriptor;
 import org.gradle.tooling.internal.protocol.test.InternalJvmTestRequest;
-import org.gradle.tooling.internal.provider.TestExecutionRequestAction;
+import org.gradle.tooling.internal.provider.action.TestExecutionRequestAction;
 
 import java.util.Collection;
 import java.util.Map;
diff --git a/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunnerTest.groovy b/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunnerTest.groovy
index c75a373..4577927 100644
--- a/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunnerTest.groovy
+++ b/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunnerTest.groovy
@@ -32,7 +32,7 @@
 import org.gradle.tooling.internal.protocol.InternalBuildAction
 import org.gradle.tooling.internal.protocol.InternalBuildActionFailureException
 import org.gradle.tooling.internal.protocol.InternalBuildActionVersion2
-import org.gradle.tooling.internal.provider.ClientProvidedBuildAction
+import org.gradle.tooling.internal.provider.action.ClientProvidedBuildAction
 import org.gradle.tooling.internal.provider.serialization.PayloadSerializer
 import org.gradle.tooling.internal.provider.serialization.SerializedPayload
 import spock.lang.Specification
diff --git a/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/ClientProvidedPhasedActionRunnerTest.groovy b/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/ClientProvidedPhasedActionRunnerTest.groovy
index 1674bc9..b251c33 100644
--- a/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/ClientProvidedPhasedActionRunnerTest.groovy
+++ b/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/ClientProvidedPhasedActionRunnerTest.groovy
@@ -32,7 +32,7 @@
 import org.gradle.tooling.internal.protocol.InternalBuildActionVersion2
 import org.gradle.tooling.internal.protocol.InternalPhasedAction
 import org.gradle.tooling.internal.protocol.PhasedActionResult
-import org.gradle.tooling.internal.provider.ClientProvidedPhasedAction
+import org.gradle.tooling.internal.provider.action.ClientProvidedPhasedAction
 import org.gradle.tooling.internal.provider.PhasedBuildActionResult
 import org.gradle.tooling.internal.provider.serialization.PayloadSerializer
 import org.gradle.tooling.internal.provider.serialization.SerializedPayload
diff --git a/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/TestExecutionBuildConfigurationActionTest.groovy b/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/TestExecutionBuildConfigurationActionTest.groovy
index b9e3436..a7449a0 100644
--- a/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/TestExecutionBuildConfigurationActionTest.groovy
+++ b/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/TestExecutionBuildConfigurationActionTest.groovy
@@ -33,7 +33,7 @@
 import org.gradle.internal.service.ServiceRegistry
 import org.gradle.tooling.internal.protocol.test.InternalDebugOptions
 import org.gradle.tooling.internal.protocol.test.InternalJvmTestRequest
-import org.gradle.tooling.internal.provider.TestExecutionRequestAction
+import org.gradle.tooling.internal.provider.action.TestExecutionRequestAction
 import spock.lang.Specification
 import spock.lang.Unroll
 
diff --git a/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/TestExecutionResultEvaluatorTest.groovy b/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/TestExecutionResultEvaluatorTest.groovy
index 6450144..38327ec 100644
--- a/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/TestExecutionResultEvaluatorTest.groovy
+++ b/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/TestExecutionResultEvaluatorTest.groovy
@@ -30,7 +30,7 @@
 import org.gradle.internal.operations.OperationIdentifier
 import org.gradle.internal.operations.OperationStartEvent
 import org.gradle.tooling.internal.protocol.test.InternalJvmTestRequest
-import org.gradle.tooling.internal.provider.TestExecutionRequestAction
+import org.gradle.tooling.internal.provider.action.TestExecutionRequestAction
 import spock.lang.Specification
 
 import static org.gradle.util.internal.TextUtil.normaliseLineSeparators